Go back to index | PHP CodeBrowser

phpseclib/Net/SSH2.php
  1.    1  <?php
  2.    2 
  3.    3 /**
  4.    4  * Pure-PHP implementation of SSHv2.
  5.    5  *
  6.    6  * PHP versions 4 and 5
  7.    7  *
  8.    8  * Here are some examples of how to use this library:
  9.    9  * <code>
  10.   10  * <?php
  11.   11  *    include 'Net/SSH2.php';
  12.   12  *
  13.   13  *    $ssh = new Net_SSH2('www.domain.tld');
  14.   14  *    if (!$ssh->login('username', 'password')) {
  15.   15  *        exit('Login Failed');
  16.   16  *    }
  17.   17  *
  18.   18  *    echo $ssh->exec('pwd');
  19.   19  *    echo $ssh->exec('ls -la');
  20.   20  * ?>
  21.   21  * </code>
  22.   22  *
  23.   23  * <code>
  24.   24  * <?php
  25.   25  *    include 'Crypt/RSA.php';
  26.   26  *    include 'Net/SSH2.php';
  27.   27  *
  28.   28  *    $key = new Crypt_RSA();
  29.   29  *    //$key->setPassword('whatever');
  30.   30  *    $key->loadKey(file_get_contents('privatekey'));
  31.   31  *
  32.   32  *    $ssh = new Net_SSH2('www.domain.tld');
  33.   33  *    if (!$ssh->login('username', $key)) {
  34.   34  *        exit('Login Failed');
  35.   35  *    }
  36.   36  *
  37.   37  *    echo $ssh->read('username@username:~$');
  38.   38  *    $ssh->write("ls -la\n");
  39.   39  *    echo $ssh->read('username@username:~$');
  40.   40  * ?>
  41.   41  * </code>
  42.   42  *
  43.   43  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  44.   44  * of this software and associated documentation files (the "Software"), to deal
  45.   45  * in the Software without restriction, including without limitation the rights
  46.   46  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  47.   47  * copies of the Software, and to permit persons to whom the Software is
  48.   48  * furnished to do so, subject to the following conditions:
  49.   49  *
  50.   50  * The above copyright notice and this permission notice shall be included in
  51.   51  * all copies or substantial portions of the Software.
  52.   52  *
  53.   53  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  54.   54  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  55.   55  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  56.   56  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  57.   57  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  58.   58  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  59.   59  * THE SOFTWARE.
  60.   60  *
  61.   61  * @category  Net
  62.   62  * @package   Net_SSH2
  63.   63  * @author    Jim Wigginton <terrafrost@php.net>
  64.   64  * @copyright 2007 Jim Wigginton
  65.   65  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  66.   66  * @link      http://phpseclib.sourceforge.net
  67.   67  */
  68.   68 
  69.   69 /**#@+
  70.   70  * Execution Bitmap Masks
  71.   71  *
  72.   72  * @see self::bitmap
  73.   73  * @access private
  74.   74  */
  75.   75 define('NET_SSH2_MASK_CONSTRUCTOR',   0x00000001);
  76.   76 define('NET_SSH2_MASK_CONNECTED',     0x00000002);
  77.   77 define('NET_SSH2_MASK_LOGIN_REQ',     0x00000004);
  78.   78 define('NET_SSH2_MASK_LOGIN',         0x00000008);
  79.   79 define('NET_SSH2_MASK_SHELL',         0x00000010);
  80.   80 define('NET_SSH2_MASK_WINDOW_ADJUST'0x00000020);
  81.   81 /**#@-*/
  82.   82 
  83.   83 /**#@+
  84.   84  * Channel constants
  85.   85  *
  86.   86  * RFC4254 refers not to client and server channels but rather to sender and recipient channels.  we don't refer
  87.   87  * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  88.   88  * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  89.   89  * recepient channel.  at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  90.   90  * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  91.   91  *     The 'recipient channel' is the channel number given in the original
  92.   92  *     open request, and 'sender channel' is the channel number allocated by
  93.   93  *     the other side.
  94.   94  *
  95.   95  * @see self::_send_channel_packet()
  96.   96  * @see self::_get_channel_packet()
  97.   97  * @access private
  98.   98  */
  99.   99 define('NET_SSH2_CHANNEL_EXEC',      1); // PuTTy uses 0x100
  100.  100 define('NET_SSH2_CHANNEL_SHELL',     2);
  101.  101 define('NET_SSH2_CHANNEL_SUBSYSTEM'3);
  102.  102 define('NET_SSH2_CHANNEL_AGENT_FORWARD'4);
  103.  103 /**#@-*/
  104.  104 
  105.  105 /**#@+
  106.  106  * @access public
  107.  107  * @see self::getLog()
  108.  108  */
  109.  109 /**
  110.  110  * Returns the message numbers
  111.  111  */
  112.  112 define('NET_SSH2_LOG_SIMPLE',  1);
  113.  113 /**
  114.  114  * Returns the message content
  115.  115  */
  116.  116 define('NET_SSH2_LOG_COMPLEX'2);
  117.  117 /**
  118.  118  * Outputs the content real-time
  119.  119  */
  120.  120 define('NET_SSH2_LOG_REALTIME'3);
  121.  121 /**
  122.  122  * Dumps the content real-time to a file
  123.  123  */
  124.  124 define('NET_SSH2_LOG_REALTIME_FILE'4);
  125.  125 /**
  126.  126  * Make sure that the log never gets larger than this
  127.  127  */
  128.  128 define('NET_SSH2_LOG_MAX_SIZE'1024 1024);
  129.  129 /**#@-*/
  130.  130 
  131.  131 /**#@+
  132.  132  * @access public
  133.  133  * @see self::read()
  134.  134  */
  135.  135 /**
  136.  136  * Returns when a string matching $expect exactly is found
  137.  137  */
  138.  138 define('NET_SSH2_READ_SIMPLE',  1);
  139.  139 /**
  140.  140  * Returns when a string matching the regular expression $expect is found
  141.  141  */
  142.  142 define('NET_SSH2_READ_REGEX'2);
  143.  143 /**
  144.  144  * Returns when a string matching the regular expression $expect is found
  145.  145  */
  146.  146 define('NET_SSH2_READ_NEXT'3);
  147.  147 /**#@-*/
  148.  148 
  149.  149 /**
  150.  150  * Pure-PHP implementation of SSHv2.
  151.  151  *
  152.  152  * @package Net_SSH2
  153.  153  * @author  Jim Wigginton <terrafrost@php.net>
  154.  154  * @access  public
  155.  155  */
  156.  156 class Net_SSH2
  157.  157 {
  158.  158     /**
  159.  159      * The SSH identifier
  160.  160      *
  161.  161      * @var string
  162.  162      * @access private
  163.  163      */
  164.  164     var $identifier;
  165.  165 
  166.  166     /**
  167.  167      * The Socket Object
  168.  168      *
  169.  169      * @var object
  170.  170      * @access private
  171.  171      */
  172.  172     var $fsock;
  173.  173 
  174.  174     /**
  175.  175      * Execution Bitmap
  176.  176      *
  177.  177      * The bits that are set represent functions that have been called already.  This is used to determine
  178.  178      * if a requisite function has been successfully executed.  If not, an error should be thrown.
  179.  179      *
  180.  180      * @var int
  181.  181      * @access private
  182.  182      */
  183.  183     var $bitmap 0;
  184.  184 
  185.  185     /**
  186.  186      * Error information
  187.  187      *
  188.  188      * @see self::getErrors()
  189.  189      * @see self::getLastError()
  190.  190      * @var string
  191.  191      * @access private
  192.  192      */
  193.  193     var $errors = array();
  194.  194 
  195.  195     /**
  196.  196      * Server Identifier
  197.  197      *
  198.  198      * @see self::getServerIdentification()
  199.  199      * @var array|false
  200.  200      * @access private
  201.  201      */
  202.  202     var $server_identifier false;
  203.  203 
  204.  204     /**
  205.  205      * Key Exchange Algorithms
  206.  206      *
  207.  207      * @see self::getKexAlgorithims()
  208.  208      * @var array|false
  209.  209      * @access private
  210.  210      */
  211.  211     var $kex_algorithms false;
  212.  212 
  213.  213     /**
  214.  214      * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  215.  215      *
  216.  216      * @see self::_key_exchange()
  217.  217      * @var int
  218.  218      * @access private
  219.  219      */
  220.  220     var $kex_dh_group_size_min 1536;
  221.  221 
  222.  222     /**
  223.  223      * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  224.  224      *
  225.  225      * @see self::_key_exchange()
  226.  226      * @var int
  227.  227      * @access private
  228.  228      */
  229.  229     var $kex_dh_group_size_preferred 2048;
  230.  230 
  231.  231     /**
  232.  232      * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  233.  233      *
  234.  234      * @see self::_key_exchange()
  235.  235      * @var int
  236.  236      * @access private
  237.  237      */
  238.  238     var $kex_dh_group_size_max 4096;
  239.  239 
  240.  240     /**
  241.  241      * Server Host Key Algorithms
  242.  242      *
  243.  243      * @see self::getServerHostKeyAlgorithms()
  244.  244      * @var array|false
  245.  245      * @access private
  246.  246      */
  247.  247     var $server_host_key_algorithms false;
  248.  248 
  249.  249     /**
  250.  250      * Encryption Algorithms: Client to Server
  251.  251      *
  252.  252      * @see self::getEncryptionAlgorithmsClient2Server()
  253.  253      * @var array|false
  254.  254      * @access private
  255.  255      */
  256.  256     var $encryption_algorithms_client_to_server false;
  257.  257 
  258.  258     /**
  259.  259      * Encryption Algorithms: Server to Client
  260.  260      *
  261.  261      * @see self::getEncryptionAlgorithmsServer2Client()
  262.  262      * @var array|false
  263.  263      * @access private
  264.  264      */
  265.  265     var $encryption_algorithms_server_to_client false;
  266.  266 
  267.  267     /**
  268.  268      * MAC Algorithms: Client to Server
  269.  269      *
  270.  270      * @see self::getMACAlgorithmsClient2Server()
  271.  271      * @var array|false
  272.  272      * @access private
  273.  273      */
  274.  274     var $mac_algorithms_client_to_server false;
  275.  275 
  276.  276     /**
  277.  277      * MAC Algorithms: Server to Client
  278.  278      *
  279.  279      * @see self::getMACAlgorithmsServer2Client()
  280.  280      * @var array|false
  281.  281      * @access private
  282.  282      */
  283.  283     var $mac_algorithms_server_to_client false;
  284.  284 
  285.  285     /**
  286.  286      * Compression Algorithms: Client to Server
  287.  287      *
  288.  288      * @see self::getCompressionAlgorithmsClient2Server()
  289.  289      * @var array|false
  290.  290      * @access private
  291.  291      */
  292.  292     var $compression_algorithms_client_to_server false;
  293.  293 
  294.  294     /**
  295.  295      * Compression Algorithms: Server to Client
  296.  296      *
  297.  297      * @see self::getCompressionAlgorithmsServer2Client()
  298.  298      * @var array|false
  299.  299      * @access private
  300.  300      */
  301.  301     var $compression_algorithms_server_to_client false;
  302.  302 
  303.  303     /**
  304.  304      * Languages: Server to Client
  305.  305      *
  306.  306      * @see self::getLanguagesServer2Client()
  307.  307      * @var array|false
  308.  308      * @access private
  309.  309      */
  310.  310     var $languages_server_to_client false;
  311.  311 
  312.  312     /**
  313.  313      * Languages: Client to Server
  314.  314      *
  315.  315      * @see self::getLanguagesClient2Server()
  316.  316      * @var array|false
  317.  317      * @access private
  318.  318      */
  319.  319     var $languages_client_to_server false;
  320.  320 
  321.  321     /**
  322.  322      * Block Size for Server to Client Encryption
  323.  323      *
  324.  324      * "Note that the length of the concatenation of 'packet_length',
  325.  325      *  'padding_length', 'payload', and 'random padding' MUST be a multiple
  326.  326      *  of the cipher block size or 8, whichever is larger.  This constraint
  327.  327      *  MUST be enforced, even when using stream ciphers."
  328.  328      *
  329.  329      *  -- http://tools.ietf.org/html/rfc4253#section-6
  330.  330      *
  331.  331      * @see self::Net_SSH2()
  332.  332      * @see self::_send_binary_packet()
  333.  333      * @var int
  334.  334      * @access private
  335.  335      */
  336.  336     var $encrypt_block_size 8;
  337.  337 
  338.  338     /**
  339.  339      * Block Size for Client to Server Encryption
  340.  340      *
  341.  341      * @see self::Net_SSH2()
  342.  342      * @see self::_get_binary_packet()
  343.  343      * @var int
  344.  344      * @access private
  345.  345      */
  346.  346     var $decrypt_block_size 8;
  347.  347 
  348.  348     /**
  349.  349      * Server to Client Encryption Object
  350.  350      *
  351.  351      * @see self::_get_binary_packet()
  352.  352      * @var object
  353.  353      * @access private
  354.  354      */
  355.  355     var $decrypt false;
  356.  356 
  357.  357     /**
  358.  358      * Client to Server Encryption Object
  359.  359      *
  360.  360      * @see self::_send_binary_packet()
  361.  361      * @var object
  362.  362      * @access private
  363.  363      */
  364.  364     var $encrypt false;
  365.  365 
  366.  366     /**
  367.  367      * Client to Server HMAC Object
  368.  368      *
  369.  369      * @see self::_send_binary_packet()
  370.  370      * @var object
  371.  371      * @access private
  372.  372      */
  373.  373     var $hmac_create false;
  374.  374 
  375.  375     /**
  376.  376      * Server to Client HMAC Object
  377.  377      *
  378.  378      * @see self::_get_binary_packet()
  379.  379      * @var object
  380.  380      * @access private
  381.  381      */
  382.  382     var $hmac_check false;
  383.  383 
  384.  384     /**
  385.  385      * Size of server to client HMAC
  386.  386      *
  387.  387      * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
  388.  388      * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
  389.  389      * append it.
  390.  390      *
  391.  391      * @see self::_get_binary_packet()
  392.  392      * @var int
  393.  393      * @access private
  394.  394      */
  395.  395     var $hmac_size false;
  396.  396 
  397.  397     /**
  398.  398      * Server Public Host Key
  399.  399      *
  400.  400      * @see self::getServerPublicHostKey()
  401.  401      * @var string
  402.  402      * @access private
  403.  403      */
  404.  404     var $server_public_host_key;
  405.  405 
  406.  406     /**
  407.  407      * Session identifier
  408.  408      *
  409.  409      * "The exchange hash H from the first key exchange is additionally
  410.  410      *  used as the session identifier, which is a unique identifier for
  411.  411      *  this connection."
  412.  412      *
  413.  413      *  -- http://tools.ietf.org/html/rfc4253#section-7.2
  414.  414      *
  415.  415      * @see self::_key_exchange()
  416.  416      * @var string
  417.  417      * @access private
  418.  418      */
  419.  419     var $session_id false;
  420.  420 
  421.  421     /**
  422.  422      * Exchange hash
  423.  423      *
  424.  424      * The current exchange hash
  425.  425      *
  426.  426      * @see self::_key_exchange()
  427.  427      * @var string
  428.  428      * @access private
  429.  429      */
  430.  430     var $exchange_hash false;
  431.  431 
  432.  432     /**
  433.  433      * Message Numbers
  434.  434      *
  435.  435      * @see self::Net_SSH2()
  436.  436      * @var array
  437.  437      * @access private
  438.  438      */
  439.  439     var $message_numbers = array();
  440.  440 
  441.  441     /**
  442.  442      * Disconnection Message 'reason codes' defined in RFC4253
  443.  443      *
  444.  444      * @see self::Net_SSH2()
  445.  445      * @var array
  446.  446      * @access private
  447.  447      */
  448.  448     var $disconnect_reasons = array();
  449.  449 
  450.  450     /**
  451.  451      * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  452.  452      *
  453.  453      * @see self::Net_SSH2()
  454.  454      * @var array
  455.  455      * @access private
  456.  456      */
  457.  457     var $channel_open_failure_reasons = array();
  458.  458 
  459.  459     /**
  460.  460      * Terminal Modes
  461.  461      *
  462.  462      * @link http://tools.ietf.org/html/rfc4254#section-8
  463.  463      * @see self::Net_SSH2()
  464.  464      * @var array
  465.  465      * @access private
  466.  466      */
  467.  467     var $terminal_modes = array();
  468.  468 
  469.  469     /**
  470.  470      * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  471.  471      *
  472.  472      * @link http://tools.ietf.org/html/rfc4254#section-5.2
  473.  473      * @see self::Net_SSH2()
  474.  474      * @var array
  475.  475      * @access private
  476.  476      */
  477.  477     var $channel_extended_data_type_codes = array();
  478.  478 
  479.  479     /**
  480.  480      * Send Sequence Number
  481.  481      *
  482.  482      * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
  483.  483      *
  484.  484      * @see self::_send_binary_packet()
  485.  485      * @var int
  486.  486      * @access private
  487.  487      */
  488.  488     var $send_seq_no 0;
  489.  489 
  490.  490     /**
  491.  491      * Get Sequence Number
  492.  492      *
  493.  493      * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
  494.  494      *
  495.  495      * @see self::_get_binary_packet()
  496.  496      * @var int
  497.  497      * @access private
  498.  498      */
  499.  499     var $get_seq_no 0;
  500.  500 
  501.  501     /**
  502.  502      * Server Channels
  503.  503      *
  504.  504      * Maps client channels to server channels
  505.  505      *
  506.  506      * @see self::_get_channel_packet()
  507.  507      * @see self::exec()
  508.  508      * @var array
  509.  509      * @access private
  510.  510      */
  511.  511     var $server_channels = array();
  512.  512 
  513.  513     /**
  514.  514      * Channel Buffers
  515.  515      *
  516.  516      * If a client requests a packet from one channel but receives two packets from another those packets should
  517.  517      * be placed in a buffer
  518.  518      *
  519.  519      * @see self::_get_channel_packet()
  520.  520      * @see self::exec()
  521.  521      * @var array
  522.  522      * @access private
  523.  523      */
  524.  524     var $channel_buffers = array();
  525.  525 
  526.  526     /**
  527.  527      * Channel Status
  528.  528      *
  529.  529      * Contains the type of the last sent message
  530.  530      *
  531.  531      * @see self::_get_channel_packet()
  532.  532      * @var array
  533.  533      * @access private
  534.  534      */
  535.  535     var $channel_status = array();
  536.  536 
  537.  537     /**
  538.  538      * Packet Size
  539.  539      *
  540.  540      * Maximum packet size indexed by channel
  541.  541      *
  542.  542      * @see self::_send_channel_packet()
  543.  543      * @var array
  544.  544      * @access private
  545.  545      */
  546.  546     var $packet_size_client_to_server = array();
  547.  547 
  548.  548     /**
  549.  549      * Message Number Log
  550.  550      *
  551.  551      * @see self::getLog()
  552.  552      * @var array
  553.  553      * @access private
  554.  554      */
  555.  555     var $message_number_log = array();
  556.  556 
  557.  557     /**
  558.  558      * Message Log
  559.  559      *
  560.  560      * @see self::getLog()
  561.  561      * @var array
  562.  562      * @access private
  563.  563      */
  564.  564     var $message_log = array();
  565.  565 
  566.  566     /**
  567.  567      * The Window Size
  568.  568      *
  569.  569      * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
  570.  570      *
  571.  571      * @var int
  572.  572      * @see self::_send_channel_packet()
  573.  573      * @see self::exec()
  574.  574      * @access private
  575.  575      */
  576.  576     var $window_size 0x7FFFFFFF;
  577.  577 
  578.  578     /**
  579.  579      * Window size, server to client
  580.  580      *
  581.  581      * Window size indexed by channel
  582.  582      *
  583.  583      * @see self::_send_channel_packet()
  584.  584      * @var array
  585.  585      * @access private
  586.  586      */
  587.  587     var $window_size_server_to_client = array();
  588.  588 
  589.  589     /**
  590.  590      * Window size, client to server
  591.  591      *
  592.  592      * Window size indexed by channel
  593.  593      *
  594.  594      * @see self::_get_channel_packet()
  595.  595      * @var array
  596.  596      * @access private
  597.  597      */
  598.  598     var $window_size_client_to_server = array();
  599.  599 
  600.  600     /**
  601.  601      * Server signature
  602.  602      *
  603.  603      * Verified against $this->session_id
  604.  604      *
  605.  605      * @see self::getServerPublicHostKey()
  606.  606      * @var string
  607.  607      * @access private
  608.  608      */
  609.  609     var $signature '';
  610.  610 
  611.  611     /**
  612.  612      * Server signature format
  613.  613      *
  614.  614      * ssh-rsa or ssh-dss.
  615.  615      *
  616.  616      * @see self::getServerPublicHostKey()
  617.  617      * @var string
  618.  618      * @access private
  619.  619      */
  620.  620     var $signature_format '';
  621.  621 
  622.  622     /**
  623.  623      * Interactive Buffer
  624.  624      *
  625.  625      * @see self::read()
  626.  626      * @var array
  627.  627      * @access private
  628.  628      */
  629.  629     var $interactiveBuffer '';
  630.  630 
  631.  631     /**
  632.  632      * Current log size
  633.  633      *
  634.  634      * Should never exceed NET_SSH2_LOG_MAX_SIZE
  635.  635      *
  636.  636      * @see self::_send_binary_packet()
  637.  637      * @see self::_get_binary_packet()
  638.  638      * @var int
  639.  639      * @access private
  640.  640      */
  641.  641     var $log_size;
  642.  642 
  643.  643     /**
  644.  644      * Timeout
  645.  645      *
  646.  646      * @see self::setTimeout()
  647.  647      * @access private
  648.  648      */
  649.  649     var $timeout;
  650.  650 
  651.  651     /**
  652.  652      * Current Timeout
  653.  653      *
  654.  654      * @see self::_get_channel_packet()
  655.  655      * @access private
  656.  656      */
  657.  657     var $curTimeout;
  658.  658 
  659.  659     /**
  660.  660      * Real-time log file pointer
  661.  661      *
  662.  662      * @see self::_append_log()
  663.  663      * @var resource
  664.  664      * @access private
  665.  665      */
  666.  666     var $realtime_log_file;
  667.  667 
  668.  668     /**
  669.  669      * Real-time log file size
  670.  670      *
  671.  671      * @see self::_append_log()
  672.  672      * @var int
  673.  673      * @access private
  674.  674      */
  675.  675     var $realtime_log_size;
  676.  676 
  677.  677     /**
  678.  678      * Has the signature been validated?
  679.  679      *
  680.  680      * @see self::getServerPublicHostKey()
  681.  681      * @var bool
  682.  682      * @access private
  683.  683      */
  684.  684     var $signature_validated false;
  685.  685 
  686.  686     /**
  687.  687      * Real-time log file wrap boolean
  688.  688      *
  689.  689      * @see self::_append_log()
  690.  690      * @access private
  691.  691      */
  692.  692     var $realtime_log_wrap;
  693.  693 
  694.  694     /**
  695.  695      * Flag to suppress stderr from output
  696.  696      *
  697.  697      * @see self::enableQuietMode()
  698.  698      * @access private
  699.  699      */
  700.  700     var $quiet_mode false;
  701.  701 
  702.  702     /**
  703.  703      * Time of first network activity
  704.  704      *
  705.  705      * @var int
  706.  706      * @access private
  707.  707      */
  708.  708     var $last_packet;
  709.  709 
  710.  710     /**
  711.  711      * Exit status returned from ssh if any
  712.  712      *
  713.  713      * @var int
  714.  714      * @access private
  715.  715      */
  716.  716     var $exit_status;
  717.  717 
  718.  718     /**
  719.  719      * Flag to request a PTY when using exec()
  720.  720      *
  721.  721      * @var bool
  722.  722      * @see self::enablePTY()
  723.  723      * @access private
  724.  724      */
  725.  725     var $request_pty false;
  726.  726 
  727.  727     /**
  728.  728      * Flag set while exec() is running when using enablePTY()
  729.  729      *
  730.  730      * @var bool
  731.  731      * @access private
  732.  732      */
  733.  733     var $in_request_pty_exec false;
  734.  734 
  735.  735     /**
  736.  736      * Flag set after startSubsystem() is called
  737.  737      *
  738.  738      * @var bool
  739.  739      * @access private
  740.  740      */
  741.  741     var $in_subsystem;
  742.  742 
  743.  743     /**
  744.  744      * Contents of stdError
  745.  745      *
  746.  746      * @var string
  747.  747      * @access private
  748.  748      */
  749.  749     var $stdErrorLog;
  750.  750 
  751.  751     /**
  752.  752      * The Last Interactive Response
  753.  753      *
  754.  754      * @see self::_keyboard_interactive_process()
  755.  755      * @var string
  756.  756      * @access private
  757.  757      */
  758.  758     var $last_interactive_response '';
  759.  759 
  760.  760     /**
  761.  761      * Keyboard Interactive Request / Responses
  762.  762      *
  763.  763      * @see self::_keyboard_interactive_process()
  764.  764      * @var array
  765.  765      * @access private
  766.  766      */
  767.  767     var $keyboard_requests_responses = array();
  768.  768 
  769.  769     /**
  770.  770      * Banner Message
  771.  771      *
  772.  772      * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  773.  773      * authentication may be relevant for getting legal protection."
  774.  774      *
  775.  775      * @see self::_filter()
  776.  776      * @see self::getBannerMessage()
  777.  777      * @var string
  778.  778      * @access private
  779.  779      */
  780.  780     var $banner_message '';
  781.  781 
  782.  782     /**
  783.  783      * Did read() timeout or return normally?
  784.  784      *
  785.  785      * @see self::isTimeout()
  786.  786      * @var bool
  787.  787      * @access private
  788.  788      */
  789.  789     var $is_timeout false;
  790.  790 
  791.  791     /**
  792.  792      * Log Boundary
  793.  793      *
  794.  794      * @see self::_format_log()
  795.  795      * @var string
  796.  796      * @access private
  797.  797      */
  798.  798     var $log_boundary ':';
  799.  799 
  800.  800     /**
  801.  801      * Log Long Width
  802.  802      *
  803.  803      * @see self::_format_log()
  804.  804      * @var int
  805.  805      * @access private
  806.  806      */
  807.  807     var $log_long_width 65;
  808.  808 
  809.  809     /**
  810.  810      * Log Short Width
  811.  811      *
  812.  812      * @see self::_format_log()
  813.  813      * @var int
  814.  814      * @access private
  815.  815      */
  816.  816     var $log_short_width 16;
  817.  817 
  818.  818     /**
  819.  819      * Hostname
  820.  820      *
  821.  821      * @see self::Net_SSH2()
  822.  822      * @see self::_connect()
  823.  823      * @var string
  824.  824      * @access private
  825.  825      */
  826.  826     var $host;
  827.  827 
  828.  828     /**
  829.  829      * Port Number
  830.  830      *
  831.  831      * @see self::Net_SSH2()
  832.  832      * @see self::_connect()
  833.  833      * @var int
  834.  834      * @access private
  835.  835      */
  836.  836     var $port;
  837.  837 
  838.  838     /**
  839.  839      * Number of columns for terminal window size
  840.  840      *
  841.  841      * @see self::getWindowColumns()
  842.  842      * @see self::setWindowColumns()
  843.  843      * @see self::setWindowSize()
  844.  844      * @var int
  845.  845      * @access private
  846.  846      */
  847.  847     var $windowColumns 80;
  848.  848 
  849.  849     /**
  850.  850      * Number of columns for terminal window size
  851.  851      *
  852.  852      * @see self::getWindowRows()
  853.  853      * @see self::setWindowRows()
  854.  854      * @see self::setWindowSize()
  855.  855      * @var int
  856.  856      * @access private
  857.  857      */
  858.  858     var $windowRows 24;
  859.  859 
  860.  860     /**
  861.  861      * Crypto Engine
  862.  862      *
  863.  863      * @see self::setCryptoEngine()
  864.  864      * @see self::_key_exchange()
  865.  865      * @var int
  866.  866      * @access private
  867.  867      */
  868.  868     var $crypto_engine false;
  869.  869 
  870.  870     /**
  871.  871      * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
  872.  872      *
  873.  873      * @var System_SSH_Agent
  874.  874      * @access private
  875.  875      */
  876.  876     var $agent;
  877.  877 
  878.  878     /**
  879.  879      * Send the identification string first?
  880.  880      *
  881.  881      * @var bool
  882.  882      * @access private
  883.  883      */
  884.  884     var $send_id_string_first true;
  885.  885 
  886.  886     /**
  887.  887      * Send the key exchange initiation packet first?
  888.  888      *
  889.  889      * @var bool
  890.  890      * @access private
  891.  891      */
  892.  892     var $send_kex_first true;
  893.  893 
  894.  894     /**
  895.  895      * Some versions of OpenSSH incorrectly calculate the key size
  896.  896      *
  897.  897      * @var bool
  898.  898      * @access private
  899.  899      */
  900.  900     var $bad_key_size_fix false;
  901.  901 
  902.  902     /**
  903.  903      * The selected decryption algorithm
  904.  904      *
  905.  905      * @var string
  906.  906      * @access private
  907.  907      */
  908.  908     var $decrypt_algorithm '';
  909.  909 
  910.  910     /**
  911.  911      * Should we try to re-connect to re-establish keys?
  912.  912      *
  913.  913      * @var bool
  914.  914      * @access private
  915.  915      */
  916.  916     var $retry_connect false;
  917.  917 
  918.  918     /**
  919.  919      * Binary Packet Buffer
  920.  920      *
  921.  921      * @var string|false
  922.  922      * @access private
  923.  923      */
  924.  924     var $binary_packet_buffer false;
  925.  925 
  926.  926     /**
  927.  927      * Default Constructor.
  928.  928      *
  929.  929      * $host can either be a string, representing the host, or a stream resource.
  930.  930      *
  931.  931      * @param mixed $host
  932.  932      * @param int $port
  933.  933      * @param int $timeout
  934.  934      * @see self::login()
  935.  935      * @return Net_SSH2
  936.  936      * @access public
  937.  937      */
  938.  938     function __construct($host$port 22$timeout 10)
  939.  939     {
  940.  940         // Include Math_BigInteger
  941.  941         // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  942.  942         if (!class_exists('Math_BigInteger')) {
  943.  943             include_once 'Math/BigInteger.php';
  944.  944         }
  945.  945 
  946.  946         if (!function_exists('crypt_random_string')) {
  947.  947             include_once 'Crypt/Random.php';
  948.  948         }
  949.  949 
  950.  950         if (!class_exists('Crypt_Hash')) {
  951.  951             include_once 'Crypt/Hash.php';
  952.  952         }
  953.  953 
  954.  954         // include Crypt_Base so constants can be defined for setCryptoEngine()
  955.  955         if (!class_exists('Crypt_Base')) {
  956.  956             include_once 'Crypt/Base.php';
  957.  957         }
  958.  958 
  959.  959         $this->message_numbers = array(
  960.  960             => 'NET_SSH2_MSG_DISCONNECT',
  961.  961             => 'NET_SSH2_MSG_IGNORE',
  962.  962             => 'NET_SSH2_MSG_UNIMPLEMENTED',
  963.  963             => 'NET_SSH2_MSG_DEBUG',
  964.  964             => 'NET_SSH2_MSG_SERVICE_REQUEST',
  965.  965             => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  966.  966             20 => 'NET_SSH2_MSG_KEXINIT',
  967.  967             21 => 'NET_SSH2_MSG_NEWKEYS',
  968.  968             30 => 'NET_SSH2_MSG_KEXDH_INIT',
  969.  969             31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  970.  970             50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  971.  971             51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  972.  972             52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  973.  973             53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  974.  974 
  975.  975             80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  976.  976             81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  977.  977             82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  978.  978             90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  979.  979             91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  980.  980             92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  981.  981             93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  982.  982             94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  983.  983             95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  984.  984             96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  985.  985             97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  986.  986             98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  987.  987             99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  988.  988             100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  989.  989         );
  990.  990         $this->disconnect_reasons = array(
  991.  991             => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  992.  992             => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  993.  993             => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  994.  994             => 'NET_SSH2_DISCONNECT_RESERVED',
  995.  995             => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  996.  996             => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  997.  997             => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  998.  998             => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  999.  999             => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  1000. 1000             10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  1001. 1001             11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  1002. 1002             12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  1003. 1003             13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  1004. 1004             14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  1005. 1005             15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  1006. 1006         );
  1007. 1007         $this->channel_open_failure_reasons = array(
  1008. 1008             => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  1009. 1009         );
  1010. 1010         $this->terminal_modes = array(
  1011. 1011             => 'NET_SSH2_TTY_OP_END'
  1012. 1012         );
  1013. 1013         $this->channel_extended_data_type_codes = array(
  1014. 1014             => 'NET_SSH2_EXTENDED_DATA_STDERR'
  1015. 1015         );
  1016. 1016 
  1017. 1017         $this->_define_array(
  1018. 1018             $this->message_numbers,
  1019. 1019             $this->disconnect_reasons,
  1020. 1020             $this->channel_open_failure_reasons,
  1021. 1021             $this->terminal_modes,
  1022. 1022             $this->channel_extended_data_type_codes,
  1023. 1023             array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
  1024. 1024             array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
  1025. 1025             array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  1026. 1026                   61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
  1027. 1027             // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
  1028. 1028             array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
  1029. 1029                   31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
  1030. 1030                   32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
  1031. 1031                   33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
  1032. 1032                   34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST')
  1033. 1033         );
  1034. 1034 
  1035. 1035         if (is_resource($host)) {
  1036. 1036             $this->fsock $host;
  1037. 1037             return;
  1038. 1038         }
  1039. 1039 
  1040. 1040         if (is_string($host)) {
  1041. 1041             $this->host $host;
  1042. 1042             $this->port $port;
  1043. 1043             $this->timeout $timeout;
  1044. 1044         }
  1045. 1045     }
  1046. 1046 
  1047. 1047     /**
  1048. 1048      * PHP4 compatible Default Constructor.
  1049. 1049      *
  1050. 1050      * @see self::__construct()
  1051. 1051      * @param mixed $host
  1052. 1052      * @param int $port
  1053. 1053      * @param int $timeout
  1054. 1054      * @access public
  1055. 1055      */
  1056. 1056     function Net_SSH2($host$port 22$timeout 10)
  1057. 1057     {
  1058. 1058         $this->__construct($host$port$timeout);
  1059. 1059     }
  1060. 1060 
  1061. 1061     /**
  1062. 1062      * Set Crypto Engine Mode
  1063. 1063      *
  1064. 1064      * Possible $engine values:
  1065. 1065      * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
  1066. 1066      *
  1067. 1067      * @param int $engine
  1068. 1068      * @access public
  1069. 1069      */
  1070. 1070     function setCryptoEngine($engine)
  1071. 1071     {
  1072. 1072         $this->crypto_engine $engine;
  1073. 1073     }
  1074. 1074 
  1075. 1075     /**
  1076. 1076      * Send Identification String First
  1077. 1077      *
  1078. 1078      * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1079. 1079      * both sides MUST send an identification string". It does not say which side sends it first. In
  1080. 1080      * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1081. 1081      *
  1082. 1082      * @access public
  1083. 1083      */
  1084. 1084     function sendIdentificationStringFirst()
  1085. 1085     {
  1086. 1086         $this->send_id_string_first true;
  1087. 1087     }
  1088. 1088 
  1089. 1089     /**
  1090. 1090      * Send Identification String Last
  1091. 1091      *
  1092. 1092      * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1093. 1093      * both sides MUST send an identification string". It does not say which side sends it first. In
  1094. 1094      * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1095. 1095      *
  1096. 1096      * @access public
  1097. 1097      */
  1098. 1098     function sendIdentificationStringLast()
  1099. 1099     {
  1100. 1100         $this->send_id_string_first false;
  1101. 1101     }
  1102. 1102 
  1103. 1103     /**
  1104. 1104      * Send SSH_MSG_KEXINIT First
  1105. 1105      *
  1106. 1106      * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1107. 1107      * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1108. 1108      * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1109. 1109      *
  1110. 1110      * @access public
  1111. 1111      */
  1112. 1112     function sendKEXINITFirst()
  1113. 1113     {
  1114. 1114         $this->send_kex_first true;
  1115. 1115     }
  1116. 1116 
  1117. 1117     /**
  1118. 1118      * Send SSH_MSG_KEXINIT Last
  1119. 1119      *
  1120. 1120      * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1121. 1121      * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1122. 1122      * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1123. 1123      *
  1124. 1124      * @access public
  1125. 1125      */
  1126. 1126     function sendKEXINITLast()
  1127. 1127     {
  1128. 1128         $this->send_kex_first false;
  1129. 1129     }
  1130. 1130 
  1131. 1131     /**
  1132. 1132      * Connect to an SSHv2 server
  1133. 1133      *
  1134. 1134      * @return bool
  1135. 1135      * @access private
  1136. 1136      */
  1137. 1137     function _connect()
  1138. 1138     {
  1139. 1139         if ($this->bitmap NET_SSH2_MASK_CONSTRUCTOR) {
  1140. 1140             return false;
  1141. 1141         }
  1142. 1142 
  1143. 1143         $this->bitmap |= NET_SSH2_MASK_CONSTRUCTOR;
  1144. 1144 
  1145. 1145         $this->curTimeout $this->timeout;
  1146. 1146 
  1147. 1147         $this->last_packet strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5
  1148. 1148 
  1149. 1149         if (!is_resource($this->fsock)) {
  1150. 1150             $start strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1151. 1151             // with stream_select a timeout of 0 means that no timeout takes place;
  1152. 1152             // with fsockopen a timeout of 0 means that you instantly timeout
  1153. 1153             // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
  1154. 1154             $this->fsock = @fsockopen($this->host$this->port$errno$errstr$this->curTimeout == 100000 $this->curTimeout);
  1155. 1155             if (!$this->fsock) {
  1156. 1156                 $host $this->host ':' $this->port;
  1157. 1157                 user_error(rtrim("Cannot connect to $host. Error $errno$errstr"));
  1158. 1158                 return false;
  1159. 1159             }
  1160. 1160             $elapsed strtok(microtime(), ' ') + strtok('') - $start;
  1161. 1161 
  1162. 1162             $this->curTimeout-= $elapsed;
  1163. 1163 
  1164. 1164             if ($this->curTimeout <= 0) {
  1165. 1165                 $this->is_timeout true;
  1166. 1166                 return false;
  1167. 1167             }
  1168. 1168         }
  1169. 1169 
  1170. 1170         $this->identifier $this->_generate_identifier();
  1171. 1171 
  1172. 1172         if ($this->send_id_string_first) {
  1173. 1173             fputs($this->fsock$this->identifier "\r\n");
  1174. 1174         }
  1175. 1175 
  1176. 1176         /* According to the SSH2 specs,
  1177. 1177 
  1178. 1178           "The server MAY send other lines of data before sending the version
  1179. 1179            string.  Each line SHOULD be terminated by a Carriage Return and Line
  1180. 1180            Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  1181. 1181            in ISO-10646 UTF-8 [RFC3629] (language is not specified).  Clients
  1182. 1182            MUST be able to process such lines." */
  1183. 1183         $temp '';
  1184. 1184         $extra '';
  1185. 1185         while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#'$temp$matches)) {
  1186. 1186             if (substr($temp, -2) == "\r\n") {
  1187. 1187                 $extra.= $temp;
  1188. 1188                 $temp '';
  1189. 1189             }
  1190. 1190 
  1191. 1191             if ($this->curTimeout) {
  1192. 1192                 if ($this->curTimeout 0) {
  1193. 1193                     $this->is_timeout true;
  1194. 1194                     return false;
  1195. 1195                 }
  1196. 1196                 $read = array($this->fsock);
  1197. 1197                 $write $except null;
  1198. 1198                 $start strtok(microtime(), ' ') + strtok('');
  1199. 1199                 $sec floor($this->curTimeout);
  1200. 1200                 $usec 1000000 * ($this->curTimeout $sec);
  1201. 1201                 // on windows this returns a "Warning: Invalid CRT parameters detected" error
  1202. 1202                 // the !count() is done as a workaround for <https://bugs.php.net/42682>
  1203. 1203                 if (!@stream_select($read$write$except$sec$usec) && !count($read)) {
  1204. 1204                     $this->is_timeout true;
  1205. 1205                     return false;
  1206. 1206                 }
  1207. 1207                 $elapsed strtok(microtime(), ' ') + strtok('') - $start;
  1208. 1208                 $this->curTimeout-= $elapsed;
  1209. 1209             }
  1210. 1210 
  1211. 1211             $temp.= fgets($this->fsock255);
  1212. 1212         }
  1213. 1213 
  1214. 1214         if (feof($this->fsock)) {
  1215. 1215             user_error('Connection closed by server');
  1216. 1216             return false;
  1217. 1217         }
  1218. 1218 
  1219. 1219         if (defined('NET_SSH2_LOGGING')) {
  1220. 1220             $this->_append_log('<-'$extra $temp);
  1221. 1221             $this->_append_log('->'$this->identifier "\r\n");
  1222. 1222         }
  1223. 1223 
  1224. 1224         $this->server_identifier trim($temp"\r\n");
  1225. 1225         if (strlen($extra)) {
  1226. 1226             $this->errors[] = utf8_decode($extra);
  1227. 1227         }
  1228. 1228 
  1229. 1229         if (version_compare($matches[1], '1.99''<')) {
  1230. 1230             user_error("Cannot connect to SSH $matches[1] servers");
  1231. 1231             return false;
  1232. 1232         }
  1233. 1233 
  1234. 1234         if (!$this->send_id_string_first) {
  1235. 1235             fputs($this->fsock$this->identifier "\r\n");
  1236. 1236         }
  1237. 1237 
  1238. 1238         if (!$this->send_kex_first) {
  1239. 1239             $response $this->_get_binary_packet();
  1240. 1240             if ($response === false) {
  1241. 1241                 user_error('Connection closed by server');
  1242. 1242                 return false;
  1243. 1243             }
  1244. 1244 
  1245. 1245             if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  1246. 1246                 user_error('Expected SSH_MSG_KEXINIT');
  1247. 1247                 return false;
  1248. 1248             }
  1249. 1249 
  1250. 1250             if (!$this->_key_exchange($response)) {
  1251. 1251                 return false;
  1252. 1252             }
  1253. 1253         }
  1254. 1254 
  1255. 1255         if ($this->send_kex_first && !$this->_key_exchange()) {
  1256. 1256             return false;
  1257. 1257         }
  1258. 1258 
  1259. 1259         $this->bitmap|= NET_SSH2_MASK_CONNECTED;
  1260. 1260 
  1261. 1261         return true;
  1262. 1262     }
  1263. 1263 
  1264. 1264     /**
  1265. 1265      * Generates the SSH identifier
  1266. 1266      *
  1267. 1267      * You should overwrite this method in your own class if you want to use another identifier
  1268. 1268      *
  1269. 1269      * @access protected
  1270. 1270      * @return string
  1271. 1271      */
  1272. 1272     function _generate_identifier()
  1273. 1273     {
  1274. 1274         $identifier 'SSH-2.0-phpseclib_1.0';
  1275. 1275 
  1276. 1276         $ext = array();
  1277. 1277         if (extension_loaded('openssl')) {
  1278. 1278             $ext[] = 'openssl';
  1279. 1279         } elseif (extension_loaded('mcrypt')) {
  1280. 1280             $ext[] = 'mcrypt';
  1281. 1281         }
  1282. 1282 
  1283. 1283         if (extension_loaded('gmp')) {
  1284. 1284             $ext[] = 'gmp';
  1285. 1285         } elseif (extension_loaded('bcmath')) {
  1286. 1286             $ext[] = 'bcmath';
  1287. 1287         }
  1288. 1288 
  1289. 1289         if (!empty($ext)) {
  1290. 1290             $identifier .= ' (' implode(', '$ext) . ')';
  1291. 1291         }
  1292. 1292 
  1293. 1293         return $identifier;
  1294. 1294     }
  1295. 1295 
  1296. 1296     /**
  1297. 1297      * Key Exchange
  1298. 1298      *
  1299. 1299      * @param string $kexinit_payload_server optional
  1300. 1300      * @access private
  1301. 1301      */
  1302. 1302     function _key_exchange($kexinit_payload_server false)
  1303. 1303     {
  1304. 1304         static $kex_algorithms = array(
  1305. 1305             'diffie-hellman-group1-sha1'// REQUIRED
  1306. 1306             'diffie-hellman-group14-sha1'// REQUIRED
  1307. 1307             'diffie-hellman-group-exchange-sha1'// RFC 4419
  1308. 1308             'diffie-hellman-group-exchange-sha256'// RFC 4419
  1309. 1309         );
  1310. 1310 
  1311. 1311         static $server_host_key_algorithms = array(
  1312. 1312             'ssh-rsa'// RECOMMENDED  sign   Raw RSA Key
  1313. 1313             'ssh-dss'  // REQUIRED     sign   Raw DSS Key
  1314. 1314         );
  1315. 1315 
  1316. 1316         static $encryption_algorithms false;
  1317. 1317         if ($encryption_algorithms === false) {
  1318. 1318             $encryption_algorithms = array(
  1319. 1319                 // from <http://tools.ietf.org/html/rfc4345#section-4>:
  1320. 1320                 'arcfour256',
  1321. 1321                 'arcfour128',
  1322. 1322 
  1323. 1323                 //'arcfour',      // OPTIONAL          the ARCFOUR stream cipher with a 128-bit key
  1324. 1324 
  1325. 1325                 // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
  1326. 1326                 'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
  1327. 1327                 'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
  1328. 1328                 'aes256-ctr',     // RECOMMENDED       AES with 256-bit key
  1329. 1329 
  1330. 1330                 'twofish128-ctr'// OPTIONAL          Twofish in SDCTR mode, with 128-bit key
  1331. 1331                 'twofish192-ctr'// OPTIONAL          Twofish with 192-bit key
  1332. 1332                 'twofish256-ctr'// OPTIONAL          Twofish with 256-bit key
  1333. 1333 
  1334. 1334                 'aes128-cbc',     // RECOMMENDED       AES with a 128-bit key
  1335. 1335                 'aes192-cbc',     // OPTIONAL          AES with a 192-bit key
  1336. 1336                 'aes256-cbc',     // OPTIONAL          AES in CBC mode, with a 256-bit key
  1337. 1337 
  1338. 1338                 'twofish128-cbc'// OPTIONAL          Twofish with a 128-bit key
  1339. 1339                 'twofish192-cbc'// OPTIONAL          Twofish with a 192-bit key
  1340. 1340                 'twofish256-cbc',
  1341. 1341                 'twofish-cbc',    // OPTIONAL          alias for "twofish256-cbc"
  1342. 1342                                   //                   (this is being retained for historical reasons)
  1343. 1343 
  1344. 1344                 'blowfish-ctr',   // OPTIONAL          Blowfish in SDCTR mode
  1345. 1345 
  1346. 1346                 'blowfish-cbc',   // OPTIONAL          Blowfish in CBC mode
  1347. 1347 
  1348. 1348                 '3des-ctr',       // RECOMMENDED       Three-key 3DES in SDCTR mode
  1349. 1349 
  1350. 1350                 '3des-cbc',       // REQUIRED          three-key 3DES in CBC mode
  1351. 1351                  //'none'         // OPTIONAL          no encryption; NOT RECOMMENDED
  1352. 1352             );
  1353. 1353 
  1354. 1354             if (extension_loaded('openssl') && !extension_loaded('mcrypt')) {
  1355. 1355                 // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to
  1356. 1356                 // instances that do not use continuous buffers
  1357. 1357                 $encryption_algorithms array_diff(
  1358. 1358                     $encryption_algorithms,
  1359. 1359                     array('arcfour256''arcfour128''arcfour')
  1360. 1360                 );
  1361. 1361             }
  1362. 1362 
  1363. 1363             if (phpseclib_resolve_include_path('Crypt/RC4.php') === false) {
  1364. 1364                 $encryption_algorithms array_diff(
  1365. 1365                     $encryption_algorithms,
  1366. 1366                     array('arcfour256''arcfour128''arcfour')
  1367. 1367                 );
  1368. 1368             }
  1369. 1369             if (phpseclib_resolve_include_path('Crypt/Rijndael.php') === false) {
  1370. 1370                 $encryption_algorithms array_diff(
  1371. 1371                     $encryption_algorithms,
  1372. 1372                     array('aes128-ctr''aes192-ctr''aes256-ctr''aes128-cbc''aes192-cbc''aes256-cbc')
  1373. 1373                 );
  1374. 1374             }
  1375. 1375             if (phpseclib_resolve_include_path('Crypt/Twofish.php') === false) {
  1376. 1376                 $encryption_algorithms array_diff(
  1377. 1377                     $encryption_algorithms,
  1378. 1378                     array('twofish128-ctr''twofish192-ctr''twofish256-ctr''twofish128-cbc''twofish192-cbc''twofish256-cbc''twofish-cbc')
  1379. 1379                 );
  1380. 1380             }
  1381. 1381             if (phpseclib_resolve_include_path('Crypt/Blowfish.php') === false) {
  1382. 1382                 $encryption_algorithms array_diff(
  1383. 1383                     $encryption_algorithms,
  1384. 1384                     array('blowfish-ctr''blowfish-cbc')
  1385. 1385                 );
  1386. 1386             }
  1387. 1387             if (phpseclib_resolve_include_path('Crypt/TripleDES.php') === false) {
  1388. 1388                 $encryption_algorithms array_diff(
  1389. 1389                     $encryption_algorithms,
  1390. 1390                     array('3des-ctr''3des-cbc')
  1391. 1391                 );
  1392. 1392             }
  1393. 1393             $encryption_algorithms array_values($encryption_algorithms);
  1394. 1394         }
  1395. 1395 
  1396. 1396         $mac_algorithms = array(
  1397. 1397             // from <http://www.ietf.org/rfc/rfc6668.txt>:
  1398. 1398             'hmac-sha2-256',// RECOMMENDED     HMAC-SHA256 (digest length = key length = 32)
  1399. 1399 
  1400. 1400             'hmac-sha1-96'// RECOMMENDED     first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
  1401. 1401             'hmac-sha1',    // REQUIRED        HMAC-SHA1 (digest length = key length = 20)
  1402. 1402             'hmac-md5-96',  // OPTIONAL        first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
  1403. 1403             'hmac-md5',     // OPTIONAL        HMAC-MD5 (digest length = key length = 16)
  1404. 1404             //'none'          // OPTIONAL        no MAC; NOT RECOMMENDED
  1405. 1405         );
  1406. 1406 
  1407. 1407         static $compression_algorithms = array(
  1408. 1408             'none'   // REQUIRED        no compression
  1409. 1409             //'zlib' // OPTIONAL        ZLIB (LZ77) compression
  1410. 1410         );
  1411. 1411 
  1412. 1412         // some SSH servers have buggy implementations of some of the above algorithms
  1413. 1413         switch (true) {
  1414. 1414             case $this->server_identifier == 'SSH-2.0-SSHD':
  1415. 1415             case substr($this->server_identifier013) == 'SSH-2.0-DLINK':
  1416. 1416                 $mac_algorithms array_values(array_diff(
  1417. 1417                     $mac_algorithms,
  1418. 1418                     array('hmac-sha1-96''hmac-md5-96')
  1419. 1419                 ));
  1420. 1420         }
  1421. 1421 
  1422. 1422         static $str_kex_algorithms$str_server_host_key_algorithms,
  1423. 1423                $encryption_algorithms_server_to_client$mac_algorithms_server_to_client$compression_algorithms_server_to_client,
  1424. 1424                $encryption_algorithms_client_to_server$mac_algorithms_client_to_server$compression_algorithms_client_to_server;
  1425. 1425 
  1426. 1426         if (empty($str_kex_algorithms)) {
  1427. 1427             $str_kex_algorithms implode(','$kex_algorithms);
  1428. 1428             $str_server_host_key_algorithms implode(','$server_host_key_algorithms);
  1429. 1429             $encryption_algorithms_server_to_client $encryption_algorithms_client_to_server implode(','$encryption_algorithms);
  1430. 1430             $mac_algorithms_server_to_client $mac_algorithms_client_to_server implode(','$mac_algorithms);
  1431. 1431             $compression_algorithms_server_to_client $compression_algorithms_client_to_server implode(','$compression_algorithms);
  1432. 1432         }
  1433. 1433 
  1434. 1434         $client_cookie crypt_random_string(16);
  1435. 1435 
  1436. 1436         $kexinit_payload_client pack(
  1437. 1437             'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
  1438. 1438             NET_SSH2_MSG_KEXINIT,
  1439. 1439             $client_cookie,
  1440. 1440             strlen($str_kex_algorithms),
  1441. 1441             $str_kex_algorithms,
  1442. 1442             strlen($str_server_host_key_algorithms),
  1443. 1443             $str_server_host_key_algorithms,
  1444. 1444             strlen($encryption_algorithms_client_to_server),
  1445. 1445             $encryption_algorithms_client_to_server,
  1446. 1446             strlen($encryption_algorithms_server_to_client),
  1447. 1447             $encryption_algorithms_server_to_client,
  1448. 1448             strlen($mac_algorithms_client_to_server),
  1449. 1449             $mac_algorithms_client_to_server,
  1450. 1450             strlen($mac_algorithms_server_to_client),
  1451. 1451             $mac_algorithms_server_to_client,
  1452. 1452             strlen($compression_algorithms_client_to_server),
  1453. 1453             $compression_algorithms_client_to_server,
  1454. 1454             strlen($compression_algorithms_server_to_client),
  1455. 1455             $compression_algorithms_server_to_client,
  1456. 1456             0,
  1457. 1457             '',
  1458. 1458             0,
  1459. 1459             '',
  1460. 1460             0,
  1461. 1461             0
  1462. 1462         );
  1463. 1463 
  1464. 1464         if ($this->send_kex_first) {
  1465. 1465             if (!$this->_send_binary_packet($kexinit_payload_client)) {
  1466. 1466                 return false;
  1467. 1467             }
  1468. 1468 
  1469. 1469             $kexinit_payload_server $this->_get_binary_packet();
  1470. 1470             if ($kexinit_payload_server === false) {
  1471. 1471                 user_error('Connection closed by server');
  1472. 1472                 return false;
  1473. 1473             }
  1474. 1474 
  1475. 1475             if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
  1476. 1476                 user_error('Expected SSH_MSG_KEXINIT');
  1477. 1477                 return false;
  1478. 1478             }
  1479. 1479         }
  1480. 1480 
  1481. 1481         $response $kexinit_payload_server;
  1482. 1482         $this->_string_shift($response1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  1483. 1483         $server_cookie $this->_string_shift($response16);
  1484. 1484 
  1485. 1485         if (strlen($response) < 4) {
  1486. 1486             return false;
  1487. 1487         }
  1488. 1488         $temp unpack('Nlength'$this->_string_shift($response4));
  1489. 1489         $this->kex_algorithms explode(','$this->_string_shift($response$temp['length']));
  1490. 1490 
  1491. 1491         if (strlen($response) < 4) {
  1492. 1492             return false;
  1493. 1493         }
  1494. 1494         $temp unpack('Nlength'$this->_string_shift($response4));
  1495. 1495         $this->server_host_key_algorithms explode(','$this->_string_shift($response$temp['length']));
  1496. 1496 
  1497. 1497         if (strlen($response) < 4) {
  1498. 1498             return false;
  1499. 1499         }
  1500. 1500         $temp unpack('Nlength'$this->_string_shift($response4));
  1501. 1501         $this->encryption_algorithms_client_to_server explode(','$this->_string_shift($response$temp['length']));
  1502. 1502 
  1503. 1503         if (strlen($response) < 4) {
  1504. 1504             return false;
  1505. 1505         }
  1506. 1506         $temp unpack('Nlength'$this->_string_shift($response4));
  1507. 1507         $this->encryption_algorithms_server_to_client explode(','$this->_string_shift($response$temp['length']));
  1508. 1508 
  1509. 1509         if (strlen($response) < 4) {
  1510. 1510             return false;
  1511. 1511         }
  1512. 1512         $temp unpack('Nlength'$this->_string_shift($response4));
  1513. 1513         $this->mac_algorithms_client_to_server explode(','$this->_string_shift($response$temp['length']));
  1514. 1514 
  1515. 1515         if (strlen($response) < 4) {
  1516. 1516             return false;
  1517. 1517         }
  1518. 1518         $temp unpack('Nlength'$this->_string_shift($response4));
  1519. 1519         $this->mac_algorithms_server_to_client explode(','$this->_string_shift($response$temp['length']));
  1520. 1520 
  1521. 1521         if (strlen($response) < 4) {
  1522. 1522             return false;
  1523. 1523         }
  1524. 1524         $temp unpack('Nlength'$this->_string_shift($response4));
  1525. 1525         $this->compression_algorithms_client_to_server explode(','$this->_string_shift($response$temp['length']));
  1526. 1526 
  1527. 1527         if (strlen($response) < 4) {
  1528. 1528             return false;
  1529. 1529         }
  1530. 1530         $temp unpack('Nlength'$this->_string_shift($response4));
  1531. 1531         $this->compression_algorithms_server_to_client explode(','$this->_string_shift($response$temp['length']));
  1532. 1532 
  1533. 1533         if (strlen($response) < 4) {
  1534. 1534             return false;
  1535. 1535         }
  1536. 1536         $temp unpack('Nlength'$this->_string_shift($response4));
  1537. 1537         $this->languages_client_to_server explode(','$this->_string_shift($response$temp['length']));
  1538. 1538 
  1539. 1539         if (strlen($response) < 4) {
  1540. 1540             return false;
  1541. 1541         }
  1542. 1542         $temp unpack('Nlength'$this->_string_shift($response4));
  1543. 1543         $this->languages_server_to_client explode(','$this->_string_shift($response$temp['length']));
  1544. 1544 
  1545. 1545         if (!strlen($response)) {
  1546. 1546             return false;
  1547. 1547         }
  1548. 1548         extract(unpack('Cfirst_kex_packet_follows'$this->_string_shift($response1)));
  1549. 1549         $first_kex_packet_follows $first_kex_packet_follows != 0;
  1550. 1550 
  1551. 1551         if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
  1552. 1552             return false;
  1553. 1553         }
  1554. 1554 
  1555. 1555         // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  1556. 1556         // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  1557. 1557         // diffie-hellman key exchange as fast as possible
  1558. 1558         $decrypt $this->_array_intersect_first($encryption_algorithms$this->encryption_algorithms_server_to_client);
  1559. 1559         $decryptKeyLength $this->_encryption_algorithm_to_key_size($decrypt);
  1560. 1560         if ($decryptKeyLength === null) {
  1561. 1561             user_error('No compatible server to client encryption algorithms found');
  1562. 1562             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1563. 1563         }
  1564. 1564 
  1565. 1565         $encrypt $this->_array_intersect_first($encryption_algorithms$this->encryption_algorithms_client_to_server);
  1566. 1566         $encryptKeyLength $this->_encryption_algorithm_to_key_size($encrypt);
  1567. 1567         if ($encryptKeyLength === null) {
  1568. 1568             user_error('No compatible client to server encryption algorithms found');
  1569. 1569             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1570. 1570         }
  1571. 1571 
  1572. 1572         $keyLength $decryptKeyLength $encryptKeyLength $decryptKeyLength $encryptKeyLength;
  1573. 1573 
  1574. 1574         // through diffie-hellman key exchange a symmetric key is obtained
  1575. 1575         $kex_algorithm $this->_array_intersect_first($kex_algorithms$this->kex_algorithms);
  1576. 1576         if ($kex_algorithm === false) {
  1577. 1577             user_error('No compatible key exchange algorithms found');
  1578. 1578             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1579. 1579         }
  1580. 1580         if (strpos($kex_algorithm'diffie-hellman-group-exchange') === 0) {
  1581. 1581             $dh_group_sizes_packed pack(
  1582. 1582                 'NNN',
  1583. 1583                 $this->kex_dh_group_size_min,
  1584. 1584                 $this->kex_dh_group_size_preferred,
  1585. 1585                 $this->kex_dh_group_size_max
  1586. 1586             );
  1587. 1587             $packet pack(
  1588. 1588                 'Ca*',
  1589. 1589                 NET_SSH2_MSG_KEXDH_GEX_REQUEST,
  1590. 1590                 $dh_group_sizes_packed
  1591. 1591             );
  1592. 1592             if (!$this->_send_binary_packet($packet)) {
  1593. 1593                 return false;
  1594. 1594             }
  1595. 1595 
  1596. 1596             $response $this->_get_binary_packet();
  1597. 1597             if ($response === false) {
  1598. 1598                 user_error('Connection closed by server');
  1599. 1599                 return false;
  1600. 1600             }
  1601. 1601             if (!strlen($response)) {
  1602. 1602                 return false;
  1603. 1603             }
  1604. 1604             extract(unpack('Ctype'$this->_string_shift($response1)));
  1605. 1605             if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
  1606. 1606                 user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
  1607. 1607                 return false;
  1608. 1608             }
  1609. 1609 
  1610. 1610             if (strlen($response) < 4) {
  1611. 1611                 return false;
  1612. 1612             }
  1613. 1613             extract(unpack('NprimeLength'$this->_string_shift($response4)));
  1614. 1614             $primeBytes $this->_string_shift($response$primeLength);
  1615. 1615             $prime = new Math_BigInteger($primeBytes, -256);
  1616. 1616 
  1617. 1617             if (strlen($response) < 4) {
  1618. 1618                 return false;
  1619. 1619             }
  1620. 1620             extract(unpack('NgLength'$this->_string_shift($response4)));
  1621. 1621             $gBytes $this->_string_shift($response$gLength);
  1622. 1622             $g = new Math_BigInteger($gBytes, -256);
  1623. 1623 
  1624. 1624             $exchange_hash_rfc4419 pack(
  1625. 1625                 'a*Na*Na*',
  1626. 1626                 $dh_group_sizes_packed,
  1627. 1627                 $primeLength,
  1628. 1628                 $primeBytes,
  1629. 1629                 $gLength,
  1630. 1630                 $gBytes
  1631. 1631             );
  1632. 1632 
  1633. 1633             $clientKexInitMessage NET_SSH2_MSG_KEXDH_GEX_INIT;
  1634. 1634             $serverKexReplyMessage NET_SSH2_MSG_KEXDH_GEX_REPLY;
  1635. 1635         } else {
  1636. 1636             switch ($kex_algorithm) {
  1637. 1637                 // see http://tools.ietf.org/html/rfc2409#section-6.2 and
  1638. 1638                 // http://tools.ietf.org/html/rfc2412, appendex E
  1639. 1639                 case 'diffie-hellman-group1-sha1':
  1640. 1640                     $prime 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  1641. 1641                             '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  1642. 1642                             '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  1643. 1643                             'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
  1644. 1644                     break;
  1645. 1645                 // see http://tools.ietf.org/html/rfc3526#section-3
  1646. 1646                 case 'diffie-hellman-group14-sha1':
  1647. 1647                     $prime 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  1648. 1648                             '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  1649. 1649                             '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  1650. 1650                             'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
  1651. 1651                             '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
  1652. 1652                             '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
  1653. 1653                             'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
  1654. 1654                             '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
  1655. 1655                     break;
  1656. 1656             }
  1657. 1657             // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
  1658. 1658             // the generator field element is 2 (decimal) and the hash function is sha1.
  1659. 1659             $g = new Math_BigInteger(2);
  1660. 1660             $prime = new Math_BigInteger($prime16);
  1661. 1661             $exchange_hash_rfc4419 '';
  1662. 1662             $clientKexInitMessage NET_SSH2_MSG_KEXDH_INIT;
  1663. 1663             $serverKexReplyMessage NET_SSH2_MSG_KEXDH_REPLY;
  1664. 1664         }
  1665. 1665 
  1666. 1666         switch ($kex_algorithm) {
  1667. 1667             case 'diffie-hellman-group-exchange-sha256':
  1668. 1668                 $kexHash = new Crypt_Hash('sha256');
  1669. 1669                 break;
  1670. 1670             default:
  1671. 1671                 $kexHash = new Crypt_Hash('sha1');
  1672. 1672         }
  1673. 1673 
  1674. 1674         /* To increase the speed of the key exchange, both client and server may
  1675. 1675            reduce the size of their private exponents.  It should be at least
  1676. 1676            twice as long as the key material that is generated from the shared
  1677. 1677            secret.  For more details, see the paper by van Oorschot and Wiener
  1678. 1678            [VAN-OORSCHOT].
  1679. 1679 
  1680. 1680            -- http://tools.ietf.org/html/rfc4419#section-6.2 */
  1681. 1681         $one = new Math_BigInteger(1);
  1682. 1682         $keyLength min($keyLength$kexHash->getLength());
  1683. 1683         $max $one->bitwise_leftShift(16 $keyLength); // 2 * 8 * $keyLength
  1684. 1684         $max $max->subtract($one);
  1685. 1685 
  1686. 1686         $x $one->random($one$max);
  1687. 1687         $e $g->modPow($x$prime);
  1688. 1688 
  1689. 1689         $eBytes $e->toBytes(true);
  1690. 1690         $data pack('CNa*'$clientKexInitMessagestrlen($eBytes), $eBytes);
  1691. 1691 
  1692. 1692         if (!$this->_send_binary_packet($data)) {
  1693. 1693             user_error('Connection closed by server');
  1694. 1694             return false;
  1695. 1695         }
  1696. 1696 
  1697. 1697         $response $this->_get_binary_packet();
  1698. 1698         if ($response === false) {
  1699. 1699             user_error('Connection closed by server');
  1700. 1700             return false;
  1701. 1701         }
  1702. 1702         if (!strlen($response)) {
  1703. 1703             return false;
  1704. 1704         }
  1705. 1705         extract(unpack('Ctype'$this->_string_shift($response1)));
  1706. 1706 
  1707. 1707         if ($type != $serverKexReplyMessage) {
  1708. 1708             user_error('Expected SSH_MSG_KEXDH_REPLY');
  1709. 1709             return false;
  1710. 1710         }
  1711. 1711 
  1712. 1712         if (strlen($response) < 4) {
  1713. 1713             return false;
  1714. 1714         }
  1715. 1715         $temp unpack('Nlength'$this->_string_shift($response4));
  1716. 1716         $this->server_public_host_key $server_public_host_key $this->_string_shift($response$temp['length']);
  1717. 1717 
  1718. 1718         if (strlen($server_public_host_key) < 4) {
  1719. 1719             return false;
  1720. 1720         }
  1721. 1721         $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  1722. 1722         $public_key_format $this->_string_shift($server_public_host_key$temp['length']);
  1723. 1723 
  1724. 1724         if (strlen($response) < 4) {
  1725. 1725             return false;
  1726. 1726         }
  1727. 1727         $temp unpack('Nlength'$this->_string_shift($response4));
  1728. 1728         $fBytes $this->_string_shift($response$temp['length']);
  1729. 1729         $f = new Math_BigInteger($fBytes, -256);
  1730. 1730 
  1731. 1731         if (strlen($response) < 4) {
  1732. 1732             return false;
  1733. 1733         }
  1734. 1734         $temp unpack('Nlength'$this->_string_shift($response4));
  1735. 1735         $this->signature $this->_string_shift($response$temp['length']);
  1736. 1736 
  1737. 1737         if (strlen($this->signature) < 4) {
  1738. 1738             return false;
  1739. 1739         }
  1740. 1740         $temp unpack('Nlength'$this->_string_shift($this->signature4));
  1741. 1741         $this->signature_format $this->_string_shift($this->signature$temp['length']);
  1742. 1742 
  1743. 1743         $key $f->modPow($x$prime);
  1744. 1744         $keyBytes $key->toBytes(true);
  1745. 1745 
  1746. 1746         $this->exchange_hash pack(
  1747. 1747             'Na*Na*Na*Na*Na*a*Na*Na*Na*',
  1748. 1748             strlen($this->identifier),
  1749. 1749             $this->identifier,
  1750. 1750             strlen($this->server_identifier),
  1751. 1751             $this->server_identifier,
  1752. 1752             strlen($kexinit_payload_client),
  1753. 1753             $kexinit_payload_client,
  1754. 1754             strlen($kexinit_payload_server),
  1755. 1755             $kexinit_payload_server,
  1756. 1756             strlen($this->server_public_host_key),
  1757. 1757             $this->server_public_host_key,
  1758. 1758             $exchange_hash_rfc4419,
  1759. 1759             strlen($eBytes),
  1760. 1760             $eBytes,
  1761. 1761             strlen($fBytes),
  1762. 1762             $fBytes,
  1763. 1763             strlen($keyBytes),
  1764. 1764             $keyBytes
  1765. 1765         );
  1766. 1766 
  1767. 1767         $this->exchange_hash $kexHash->hash($this->exchange_hash);
  1768. 1768 
  1769. 1769         if ($this->session_id === false) {
  1770. 1770             $this->session_id $this->exchange_hash;
  1771. 1771         }
  1772. 1772 
  1773. 1773         $server_host_key_algorithm $this->_array_intersect_first($server_host_key_algorithms$this->server_host_key_algorithms);
  1774. 1774         if ($server_host_key_algorithm === false) {
  1775. 1775             user_error('No compatible server host key algorithms found');
  1776. 1776             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1777. 1777         }
  1778. 1778 
  1779. 1779         if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) {
  1780. 1780             user_error('Server Host Key Algorithm Mismatch');
  1781. 1781             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1782. 1782         }
  1783. 1783 
  1784. 1784         $packet pack(
  1785. 1785             'C',
  1786. 1786             NET_SSH2_MSG_NEWKEYS
  1787. 1787         );
  1788. 1788 
  1789. 1789         if (!$this->_send_binary_packet($packet)) {
  1790. 1790             return false;
  1791. 1791         }
  1792. 1792 
  1793. 1793         $response $this->_get_binary_packet();
  1794. 1794 
  1795. 1795         if ($response === false) {
  1796. 1796             user_error('Connection closed by server');
  1797. 1797             return false;
  1798. 1798         }
  1799. 1799 
  1800. 1800         if (!strlen($response)) {
  1801. 1801             return false;
  1802. 1802         }
  1803. 1803         extract(unpack('Ctype'$this->_string_shift($response1)));
  1804. 1804 
  1805. 1805         if ($type != NET_SSH2_MSG_NEWKEYS) {
  1806. 1806             user_error('Expected SSH_MSG_NEWKEYS');
  1807. 1807             return false;
  1808. 1808         }
  1809. 1809 
  1810. 1810         switch ($encrypt) {
  1811. 1811             case '3des-cbc':
  1812. 1812                 if (!class_exists('Crypt_TripleDES')) {
  1813. 1813                     include_once 'Crypt/TripleDES.php';
  1814. 1814                 }
  1815. 1815                 $this->encrypt = new Crypt_TripleDES();
  1816. 1816                 // $this->encrypt_block_size = 64 / 8 == the default
  1817. 1817                 break;
  1818. 1818             case '3des-ctr':
  1819. 1819                 if (!class_exists('Crypt_TripleDES')) {
  1820. 1820                     include_once 'Crypt/TripleDES.php';
  1821. 1821                 }
  1822. 1822                 $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  1823. 1823                 // $this->encrypt_block_size = 64 / 8 == the default
  1824. 1824                 break;
  1825. 1825             case 'aes256-cbc':
  1826. 1826             case 'aes192-cbc':
  1827. 1827             case 'aes128-cbc':
  1828. 1828                 if (!class_exists('Crypt_Rijndael')) {
  1829. 1829                     include_once 'Crypt/Rijndael.php';
  1830. 1830                 }
  1831. 1831                 $this->encrypt = new Crypt_Rijndael();
  1832. 1832                 $this->encrypt_block_size 16// eg. 128 / 8
  1833. 1833                 break;
  1834. 1834             case 'aes256-ctr':
  1835. 1835             case 'aes192-ctr':
  1836. 1836             case 'aes128-ctr':
  1837. 1837                 if (!class_exists('Crypt_Rijndael')) {
  1838. 1838                     include_once 'Crypt/Rijndael.php';
  1839. 1839                 }
  1840. 1840                 $this->encrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
  1841. 1841                 $this->encrypt_block_size 16// eg. 128 / 8
  1842. 1842                 break;
  1843. 1843             case 'blowfish-cbc':
  1844. 1844                 if (!class_exists('Crypt_Blowfish')) {
  1845. 1845                     include_once 'Crypt/Blowfish.php';
  1846. 1846                 }
  1847. 1847                 $this->encrypt = new Crypt_Blowfish();
  1848. 1848                 $this->encrypt_block_size 8;
  1849. 1849                 break;
  1850. 1850             case 'blowfish-ctr':
  1851. 1851                 if (!class_exists('Crypt_Blowfish')) {
  1852. 1852                     include_once 'Crypt/Blowfish.php';
  1853. 1853                 }
  1854. 1854                 $this->encrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
  1855. 1855                 $this->encrypt_block_size 8;
  1856. 1856                 break;
  1857. 1857             case 'twofish128-cbc':
  1858. 1858             case 'twofish192-cbc':
  1859. 1859             case 'twofish256-cbc':
  1860. 1860             case 'twofish-cbc':
  1861. 1861                 if (!class_exists('Crypt_Twofish')) {
  1862. 1862                     include_once 'Crypt/Twofish.php';
  1863. 1863                 }
  1864. 1864                 $this->encrypt = new Crypt_Twofish();
  1865. 1865                 $this->encrypt_block_size 16;
  1866. 1866                 break;
  1867. 1867             case 'twofish128-ctr':
  1868. 1868             case 'twofish192-ctr':
  1869. 1869             case 'twofish256-ctr':
  1870. 1870                 if (!class_exists('Crypt_Twofish')) {
  1871. 1871                     include_once 'Crypt/Twofish.php';
  1872. 1872                 }
  1873. 1873                 $this->encrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
  1874. 1874                 $this->encrypt_block_size 16;
  1875. 1875                 break;
  1876. 1876             case 'arcfour':
  1877. 1877             case 'arcfour128':
  1878. 1878             case 'arcfour256':
  1879. 1879                 if (!class_exists('Crypt_RC4')) {
  1880. 1880                     include_once 'Crypt/RC4.php';
  1881. 1881                 }
  1882. 1882                 $this->encrypt = new Crypt_RC4();
  1883. 1883                 break;
  1884. 1884             case 'none':
  1885. 1885                 //$this->encrypt = new Crypt_Null();
  1886. 1886         }
  1887. 1887 
  1888. 1888         switch ($decrypt) {
  1889. 1889             case '3des-cbc':
  1890. 1890                 if (!class_exists('Crypt_TripleDES')) {
  1891. 1891                     include_once 'Crypt/TripleDES.php';
  1892. 1892                 }
  1893. 1893                 $this->decrypt = new Crypt_TripleDES();
  1894. 1894                 break;
  1895. 1895             case '3des-ctr':
  1896. 1896                 if (!class_exists('Crypt_TripleDES')) {
  1897. 1897                     include_once 'Crypt/TripleDES.php';
  1898. 1898                 }
  1899. 1899                 $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  1900. 1900                 break;
  1901. 1901             case 'aes256-cbc':
  1902. 1902             case 'aes192-cbc':
  1903. 1903             case 'aes128-cbc':
  1904. 1904                 if (!class_exists('Crypt_Rijndael')) {
  1905. 1905                     include_once 'Crypt/Rijndael.php';
  1906. 1906                 }
  1907. 1907                 $this->decrypt = new Crypt_Rijndael();
  1908. 1908                 $this->decrypt_block_size 16;
  1909. 1909                 break;
  1910. 1910             case 'aes256-ctr':
  1911. 1911             case 'aes192-ctr':
  1912. 1912             case 'aes128-ctr':
  1913. 1913                 if (!class_exists('Crypt_Rijndael')) {
  1914. 1914                     include_once 'Crypt/Rijndael.php';
  1915. 1915                 }
  1916. 1916                 $this->decrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
  1917. 1917                 $this->decrypt_block_size 16;
  1918. 1918                 break;
  1919. 1919             case 'blowfish-cbc':
  1920. 1920                 if (!class_exists('Crypt_Blowfish')) {
  1921. 1921                     include_once 'Crypt/Blowfish.php';
  1922. 1922                 }
  1923. 1923                 $this->decrypt = new Crypt_Blowfish();
  1924. 1924                 $this->decrypt_block_size 8;
  1925. 1925                 break;
  1926. 1926             case 'blowfish-ctr':
  1927. 1927                 if (!class_exists('Crypt_Blowfish')) {
  1928. 1928                     include_once 'Crypt/Blowfish.php';
  1929. 1929                 }
  1930. 1930                 $this->decrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
  1931. 1931                 $this->decrypt_block_size 8;
  1932. 1932                 break;
  1933. 1933             case 'twofish128-cbc':
  1934. 1934             case 'twofish192-cbc':
  1935. 1935             case 'twofish256-cbc':
  1936. 1936             case 'twofish-cbc':
  1937. 1937                 if (!class_exists('Crypt_Twofish')) {
  1938. 1938                     include_once 'Crypt/Twofish.php';
  1939. 1939                 }
  1940. 1940                 $this->decrypt = new Crypt_Twofish();
  1941. 1941                 $this->decrypt_block_size 16;
  1942. 1942                 break;
  1943. 1943             case 'twofish128-ctr':
  1944. 1944             case 'twofish192-ctr':
  1945. 1945             case 'twofish256-ctr':
  1946. 1946                 if (!class_exists('Crypt_Twofish')) {
  1947. 1947                     include_once 'Crypt/Twofish.php';
  1948. 1948                 }
  1949. 1949                 $this->decrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
  1950. 1950                 $this->decrypt_block_size 16;
  1951. 1951                 break;
  1952. 1952             case 'arcfour':
  1953. 1953             case 'arcfour128':
  1954. 1954             case 'arcfour256':
  1955. 1955                 if (!class_exists('Crypt_RC4')) {
  1956. 1956                     include_once 'Crypt/RC4.php';
  1957. 1957                 }
  1958. 1958                 $this->decrypt = new Crypt_RC4();
  1959. 1959                 break;
  1960. 1960             case 'none':
  1961. 1961                 //$this->decrypt = new Crypt_Null();
  1962. 1962         }
  1963. 1963 
  1964. 1964         $this->decrypt_algorithm $decrypt;
  1965. 1965 
  1966. 1966         $keyBytes pack('Na*'strlen($keyBytes), $keyBytes);
  1967. 1967 
  1968. 1968         if ($this->encrypt) {
  1969. 1969             if ($this->crypto_engine) {
  1970. 1970                 $this->encrypt->setEngine($this->crypto_engine);
  1971. 1971             }
  1972. 1972             $this->encrypt->enableContinuousBuffer();
  1973. 1973             $this->encrypt->disablePadding();
  1974. 1974 
  1975. 1975             $iv $kexHash->hash($keyBytes $this->exchange_hash 'A' $this->session_id);
  1976. 1976             while ($this->encrypt_block_size strlen($iv)) {
  1977. 1977                 $iv.= $kexHash->hash($keyBytes $this->exchange_hash $iv);
  1978. 1978             }
  1979. 1979             $this->encrypt->setIV(substr($iv0$this->encrypt_block_size));
  1980. 1980 
  1981. 1981             $key $kexHash->hash($keyBytes $this->exchange_hash 'C' $this->session_id);
  1982. 1982             while ($encryptKeyLength strlen($key)) {
  1983. 1983                 $key.= $kexHash->hash($keyBytes $this->exchange_hash $key);
  1984. 1984             }
  1985. 1985             $this->encrypt->setKey(substr($key0$encryptKeyLength));
  1986. 1986         }
  1987. 1987 
  1988. 1988         if ($this->decrypt) {
  1989. 1989             if ($this->crypto_engine) {
  1990. 1990                 $this->decrypt->setEngine($this->crypto_engine);
  1991. 1991             }
  1992. 1992             $this->decrypt->enableContinuousBuffer();
  1993. 1993             $this->decrypt->disablePadding();
  1994. 1994 
  1995. 1995             $iv $kexHash->hash($keyBytes $this->exchange_hash 'B' $this->session_id);
  1996. 1996             while ($this->decrypt_block_size strlen($iv)) {
  1997. 1997                 $iv.= $kexHash->hash($keyBytes $this->exchange_hash $iv);
  1998. 1998             }
  1999. 1999             $this->decrypt->setIV(substr($iv0$this->decrypt_block_size));
  2000. 2000 
  2001. 2001             $key $kexHash->hash($keyBytes $this->exchange_hash 'D' $this->session_id);
  2002. 2002             while ($decryptKeyLength strlen($key)) {
  2003. 2003                 $key.= $kexHash->hash($keyBytes $this->exchange_hash $key);
  2004. 2004             }
  2005. 2005             $this->decrypt->setKey(substr($key0$decryptKeyLength));
  2006. 2006         }
  2007. 2007 
  2008. 2008         /* The "arcfour128" algorithm is the RC4 cipher, as described in
  2009. 2009            [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
  2010. 2010            generated by the cipher MUST be discarded, and the first byte of the
  2011. 2011            first encrypted packet MUST be encrypted using the 1537th byte of
  2012. 2012            keystream.
  2013. 2013 
  2014. 2014            -- http://tools.ietf.org/html/rfc4345#section-4 */
  2015. 2015         if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
  2016. 2016             $this->encrypt->encrypt(str_repeat("\0"1536));
  2017. 2017         }
  2018. 2018         if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
  2019. 2019             $this->decrypt->decrypt(str_repeat("\0"1536));
  2020. 2020         }
  2021. 2021 
  2022. 2022         $mac_algorithm $this->_array_intersect_first($mac_algorithms$this->mac_algorithms_client_to_server);
  2023. 2023         if ($mac_algorithm === false) {
  2024. 2024             user_error('No compatible client to server message authentication algorithms found');
  2025. 2025             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2026. 2026         }
  2027. 2027 
  2028. 2028         $createKeyLength 0// ie. $mac_algorithm == 'none'
  2029. 2029         switch ($mac_algorithm) {
  2030. 2030             case 'hmac-sha2-256':
  2031. 2031                 $this->hmac_create = new Crypt_Hash('sha256');
  2032. 2032                 $createKeyLength 32;
  2033. 2033                 break;
  2034. 2034             case 'hmac-sha1':
  2035. 2035                 $this->hmac_create = new Crypt_Hash('sha1');
  2036. 2036                 $createKeyLength 20;
  2037. 2037                 break;
  2038. 2038             case 'hmac-sha1-96':
  2039. 2039                 $this->hmac_create = new Crypt_Hash('sha1-96');
  2040. 2040                 $createKeyLength 20;
  2041. 2041                 break;
  2042. 2042             case 'hmac-md5':
  2043. 2043                 $this->hmac_create = new Crypt_Hash('md5');
  2044. 2044                 $createKeyLength 16;
  2045. 2045                 break;
  2046. 2046             case 'hmac-md5-96':
  2047. 2047                 $this->hmac_create = new Crypt_Hash('md5-96');
  2048. 2048                 $createKeyLength 16;
  2049. 2049         }
  2050. 2050 
  2051. 2051         $mac_algorithm $this->_array_intersect_first($mac_algorithms$this->mac_algorithms_server_to_client);
  2052. 2052         if ($mac_algorithm === false) {
  2053. 2053             user_error('No compatible server to client message authentication algorithms found');
  2054. 2054             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2055. 2055         }
  2056. 2056 
  2057. 2057         $checkKeyLength 0;
  2058. 2058         $this->hmac_size 0;
  2059. 2059         switch ($mac_algorithm) {
  2060. 2060             case 'hmac-sha2-256':
  2061. 2061                 $this->hmac_check = new Crypt_Hash('sha256');
  2062. 2062                 $checkKeyLength 32;
  2063. 2063                 $this->hmac_size 32;
  2064. 2064                 break;
  2065. 2065             case 'hmac-sha1':
  2066. 2066                 $this->hmac_check = new Crypt_Hash('sha1');
  2067. 2067                 $checkKeyLength 20;
  2068. 2068                 $this->hmac_size 20;
  2069. 2069                 break;
  2070. 2070             case 'hmac-sha1-96':
  2071. 2071                 $this->hmac_check = new Crypt_Hash('sha1-96');
  2072. 2072                 $checkKeyLength 20;
  2073. 2073                 $this->hmac_size 12;
  2074. 2074                 break;
  2075. 2075             case 'hmac-md5':
  2076. 2076                 $this->hmac_check = new Crypt_Hash('md5');
  2077. 2077                 $checkKeyLength 16;
  2078. 2078                 $this->hmac_size 16;
  2079. 2079                 break;
  2080. 2080             case 'hmac-md5-96':
  2081. 2081                 $this->hmac_check = new Crypt_Hash('md5-96');
  2082. 2082                 $checkKeyLength 16;
  2083. 2083                 $this->hmac_size 12;
  2084. 2084         }
  2085. 2085 
  2086. 2086         $key $kexHash->hash($keyBytes $this->exchange_hash 'E' $this->session_id);
  2087. 2087         while ($createKeyLength strlen($key)) {
  2088. 2088             $key.= $kexHash->hash($keyBytes $this->exchange_hash $key);
  2089. 2089         }
  2090. 2090         $this->hmac_create->setKey(substr($key0$createKeyLength));
  2091. 2091 
  2092. 2092         $key $kexHash->hash($keyBytes $this->exchange_hash 'F' $this->session_id);
  2093. 2093         while ($checkKeyLength strlen($key)) {
  2094. 2094             $key.= $kexHash->hash($keyBytes $this->exchange_hash $key);
  2095. 2095         }
  2096. 2096         $this->hmac_check->setKey(substr($key0$checkKeyLength));
  2097. 2097 
  2098. 2098         $compression_algorithm $this->_array_intersect_first($compression_algorithms$this->compression_algorithms_server_to_client);
  2099. 2099         if ($compression_algorithm === false) {
  2100. 2100             user_error('No compatible server to client compression algorithms found');
  2101. 2101             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2102. 2102         }
  2103. 2103         $this->decompress $compression_algorithm == 'zlib';
  2104. 2104 
  2105. 2105         $compression_algorithm $this->_array_intersect_first($compression_algorithms$this->compression_algorithms_client_to_server);
  2106. 2106         if ($compression_algorithm === false) {
  2107. 2107             user_error('No compatible client to server compression algorithms found');
  2108. 2108             return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2109. 2109         }
  2110. 2110         $this->compress $compression_algorithm == 'zlib';
  2111. 2111 
  2112. 2112         return true;
  2113. 2113     }
  2114. 2114 
  2115. 2115     /**
  2116. 2116      * Maps an encryption algorithm name to the number of key bytes.
  2117. 2117      *
  2118. 2118      * @param string $algorithm Name of the encryption algorithm
  2119. 2119      * @return int|null Number of bytes as an integer or null for unknown
  2120. 2120      * @access private
  2121. 2121      */
  2122. 2122     function _encryption_algorithm_to_key_size($algorithm)
  2123. 2123     {
  2124. 2124         if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
  2125. 2125             return 16;
  2126. 2126         }
  2127. 2127 
  2128. 2128         switch ($algorithm) {
  2129. 2129             case 'none':
  2130. 2130                 return 0;
  2131. 2131             case 'aes128-cbc':
  2132. 2132             case 'aes128-ctr':
  2133. 2133             case 'arcfour':
  2134. 2134             case 'arcfour128':
  2135. 2135             case 'blowfish-cbc':
  2136. 2136             case 'blowfish-ctr':
  2137. 2137             case 'twofish128-cbc':
  2138. 2138             case 'twofish128-ctr':
  2139. 2139                 return 16;
  2140. 2140             case '3des-cbc':
  2141. 2141             case '3des-ctr':
  2142. 2142             case 'aes192-cbc':
  2143. 2143             case 'aes192-ctr':
  2144. 2144             case 'twofish192-cbc':
  2145. 2145             case 'twofish192-ctr':
  2146. 2146                 return 24;
  2147. 2147             case 'aes256-cbc':
  2148. 2148             case 'aes256-ctr':
  2149. 2149             case 'arcfour256':
  2150. 2150             case 'twofish-cbc':
  2151. 2151             case 'twofish256-cbc':
  2152. 2152             case 'twofish256-ctr':
  2153. 2153                 return 32;
  2154. 2154         }
  2155. 2155         return null;
  2156. 2156     }
  2157. 2157 
  2158. 2158     /**
  2159. 2159      * Tests whether or not proposed algorithm has a potential for issues
  2160. 2160      *
  2161. 2161      * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
  2162. 2162      * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
  2163. 2163      * @param string $algorithm Name of the encryption algorithm
  2164. 2164      * @return bool
  2165. 2165      * @access private
  2166. 2166      */
  2167. 2167     function _bad_algorithm_candidate($algorithm)
  2168. 2168     {
  2169. 2169         switch ($algorithm) {
  2170. 2170             case 'arcfour256':
  2171. 2171             case 'aes192-ctr':
  2172. 2172             case 'aes256-ctr':
  2173. 2173                 return true;
  2174. 2174         }
  2175. 2175 
  2176. 2176         return false;
  2177. 2177     }
  2178. 2178 
  2179. 2179     /**
  2180. 2180      * Login
  2181. 2181      *
  2182. 2182      * The $password parameter can be a plaintext password, a Crypt_RSA object or an array
  2183. 2183      *
  2184. 2184      * @param string $username
  2185. 2185      * @param mixed $password
  2186. 2186      * @param mixed $...
  2187. 2187      * @return bool
  2188. 2188      * @see self::_login()
  2189. 2189      * @access public
  2190. 2190      */
  2191. 2191     function login($username)
  2192. 2192     {
  2193. 2193         $args func_get_args();
  2194. 2194         return call_user_func_array(array(&$this'_login'), $args);
  2195. 2195     }
  2196. 2196 
  2197. 2197     /**
  2198. 2198      * Login Helper
  2199. 2199      *
  2200. 2200      * @param string $username
  2201. 2201      * @param mixed $password
  2202. 2202      * @param mixed $...
  2203. 2203      * @return bool
  2204. 2204      * @see self::_login_helper()
  2205. 2205      * @access private
  2206. 2206      */
  2207. 2207     function _login($username)
  2208. 2208     {
  2209. 2209         if (!($this->bitmap NET_SSH2_MASK_CONSTRUCTOR)) {
  2210. 2210             if (!$this->_connect()) {
  2211. 2211                 return false;
  2212. 2212             }
  2213. 2213         }
  2214. 2214 
  2215. 2215         $args array_slice(func_get_args(), 1);
  2216. 2216         if (empty($args)) {
  2217. 2217             return $this->_login_helper($username);
  2218. 2218         }
  2219. 2219 
  2220. 2220         foreach ($args as $arg) {
  2221. 2221             if ($this->_login_helper($username$arg)) {
  2222. 2222                 return true;
  2223. 2223             }
  2224. 2224         }
  2225. 2225         return false;
  2226. 2226     }
  2227. 2227 
  2228. 2228     /**
  2229. 2229      * Login Helper
  2230. 2230      *
  2231. 2231      * @param string $username
  2232. 2232      * @param string $password
  2233. 2233      * @return bool
  2234. 2234      * @access private
  2235. 2235      * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  2236. 2236      *           by sending dummy SSH_MSG_IGNORE messages.
  2237. 2237      */
  2238. 2238     function _login_helper($username$password null)
  2239. 2239     {
  2240. 2240         if (!($this->bitmap NET_SSH2_MASK_CONNECTED)) {
  2241. 2241             return false;
  2242. 2242         }
  2243. 2243 
  2244. 2244         if (!($this->bitmap NET_SSH2_MASK_LOGIN_REQ)) {
  2245. 2245             $packet pack(
  2246. 2246                 'CNa*',
  2247. 2247                 NET_SSH2_MSG_SERVICE_REQUEST,
  2248. 2248                 strlen('ssh-userauth'),
  2249. 2249                 'ssh-userauth'
  2250. 2250             );
  2251. 2251 
  2252. 2252             if (!$this->_send_binary_packet($packet)) {
  2253. 2253                 return false;
  2254. 2254             }
  2255. 2255 
  2256. 2256             $response $this->_get_binary_packet();
  2257. 2257             if ($response === false) {
  2258. 2258                 if ($this->retry_connect) {
  2259. 2259                     $this->retry_connect false;
  2260. 2260                     if (!$this->_connect()) {
  2261. 2261                         return false;
  2262. 2262                     }
  2263. 2263                     return $this->_login_helper($username$password);
  2264. 2264                 }
  2265. 2265                 user_error('Connection closed by server');
  2266. 2266                 return false;
  2267. 2267             }
  2268. 2268 
  2269. 2269             if (strlen($response) < 4) {
  2270. 2270                 return false;
  2271. 2271             }
  2272. 2272             extract(unpack('Ctype'$this->_string_shift($response1)));
  2273. 2273 
  2274. 2274             if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
  2275. 2275                 user_error('Expected SSH_MSG_SERVICE_ACCEPT');
  2276. 2276                 return false;
  2277. 2277             }
  2278. 2278             $this->bitmap |= NET_SSH2_MASK_LOGIN_REQ;
  2279. 2279         }
  2280. 2280 
  2281. 2281         if (strlen($this->last_interactive_response)) {
  2282. 2282             return !is_string($password) && !is_array($password) ? false $this->_keyboard_interactive_process($password);
  2283. 2283         }
  2284. 2284 
  2285. 2285         // although PHP5's get_class() preserves the case, PHP4's does not
  2286. 2286         if (is_object($password)) {
  2287. 2287             switch (strtolower(get_class($password))) {
  2288. 2288                 case 'crypt_rsa':
  2289. 2289                     return $this->_privatekey_login($username$password);
  2290. 2290                 case 'system_ssh_agent':
  2291. 2291                     return $this->_ssh_agent_login($username$password);
  2292. 2292             }
  2293. 2293         }
  2294. 2294 
  2295. 2295         if (is_array($password)) {
  2296. 2296             if ($this->_keyboard_interactive_login($username$password)) {
  2297. 2297                 $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2298. 2298                 return true;
  2299. 2299             }
  2300. 2300             return false;
  2301. 2301         }
  2302. 2302 
  2303. 2303         if (!isset($password)) {
  2304. 2304             $packet pack(
  2305. 2305                 'CNa*Na*Na*',
  2306. 2306                 NET_SSH2_MSG_USERAUTH_REQUEST,
  2307. 2307                 strlen($username),
  2308. 2308                 $username,
  2309. 2309                 strlen('ssh-connection'),
  2310. 2310                 'ssh-connection',
  2311. 2311                 strlen('none'),
  2312. 2312                 'none'
  2313. 2313             );
  2314. 2314 
  2315. 2315             if (!$this->_send_binary_packet($packet)) {
  2316. 2316                 return false;
  2317. 2317             }
  2318. 2318 
  2319. 2319             $response $this->_get_binary_packet();
  2320. 2320             if ($response === false) {
  2321. 2321                 user_error('Connection closed by server');
  2322. 2322                 return false;
  2323. 2323             }
  2324. 2324 
  2325. 2325             if (!strlen($response)) {
  2326. 2326                 return false;
  2327. 2327             }
  2328. 2328             extract(unpack('Ctype'$this->_string_shift($response1)));
  2329. 2329 
  2330. 2330             switch ($type) {
  2331. 2331                 case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2332. 2332                     $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2333. 2333                     return true;
  2334. 2334                 //case NET_SSH2_MSG_USERAUTH_FAILURE:
  2335. 2335                 default:
  2336. 2336                     return false;
  2337. 2337             }
  2338. 2338         }
  2339. 2339 
  2340. 2340         $packet pack(
  2341. 2341             'CNa*Na*Na*CNa*',
  2342. 2342             NET_SSH2_MSG_USERAUTH_REQUEST,
  2343. 2343             strlen($username),
  2344. 2344             $username,
  2345. 2345             strlen('ssh-connection'),
  2346. 2346             'ssh-connection',
  2347. 2347             strlen('password'),
  2348. 2348             'password',
  2349. 2349             0,
  2350. 2350             strlen($password),
  2351. 2351             $password
  2352. 2352         );
  2353. 2353 
  2354. 2354         // remove the username and password from the logged packet
  2355. 2355         if (!defined('NET_SSH2_LOGGING')) {
  2356. 2356             $logged null;
  2357. 2357         } else {
  2358. 2358             $logged pack(
  2359. 2359                 'CNa*Na*Na*CNa*',
  2360. 2360                 NET_SSH2_MSG_USERAUTH_REQUEST,
  2361. 2361                 strlen('username'),
  2362. 2362                 'username',
  2363. 2363                 strlen('ssh-connection'),
  2364. 2364                 'ssh-connection',
  2365. 2365                 strlen('password'),
  2366. 2366                 'password',
  2367. 2367                 0,
  2368. 2368                 strlen('password'),
  2369. 2369                 'password'
  2370. 2370             );
  2371. 2371         }
  2372. 2372 
  2373. 2373         if (!$this->_send_binary_packet($packet$logged)) {
  2374. 2374             return false;
  2375. 2375         }
  2376. 2376 
  2377. 2377         $response $this->_get_binary_packet();
  2378. 2378         if ($response === false) {
  2379. 2379             user_error('Connection closed by server');
  2380. 2380             return false;
  2381. 2381         }
  2382. 2382 
  2383. 2383         if (!strlen($response)) {
  2384. 2384             return false;
  2385. 2385         }
  2386. 2386         extract(unpack('Ctype'$this->_string_shift($response1)));
  2387. 2387 
  2388. 2388         switch ($type) {
  2389. 2389             case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ// in theory, the password can be changed
  2390. 2390                 if (defined('NET_SSH2_LOGGING')) {
  2391. 2391                     $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
  2392. 2392                 }
  2393. 2393                 if (strlen($response) < 4) {
  2394. 2394                     return false;
  2395. 2395                 }
  2396. 2396                 extract(unpack('Nlength'$this->_string_shift($response4)));
  2397. 2397                 $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' utf8_decode($this->_string_shift($response$length));
  2398. 2398                 return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  2399. 2399             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2400. 2400                 // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
  2401. 2401                 // multi-factor authentication
  2402. 2402                 if (strlen($response) < 4) {
  2403. 2403                     return false;
  2404. 2404                 }
  2405. 2405                 extract(unpack('Nlength'$this->_string_shift($response4)));
  2406. 2406                 $auth_methods explode(','$this->_string_shift($response$length));
  2407. 2407                 if (!strlen($response)) {
  2408. 2408                     return false;
  2409. 2409                 }
  2410. 2410                 extract(unpack('Cpartial_success'$this->_string_shift($response1)));
  2411. 2411                 $partial_success $partial_success != 0;
  2412. 2412 
  2413. 2413                 if (!$partial_success && in_array('keyboard-interactive'$auth_methods)) {
  2414. 2414                     if ($this->_keyboard_interactive_login($username$password)) {
  2415. 2415                         $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2416. 2416                         return true;
  2417. 2417                     }
  2418. 2418                     return false;
  2419. 2419                 }
  2420. 2420                 return false;
  2421. 2421             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2422. 2422                 $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2423. 2423                 return true;
  2424. 2424         }
  2425. 2425 
  2426. 2426         return false;
  2427. 2427     }
  2428. 2428 
  2429. 2429     /**
  2430. 2430      * Login via keyboard-interactive authentication
  2431. 2431      *
  2432. 2432      * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
  2433. 2433      *
  2434. 2434      * @param string $username
  2435. 2435      * @param string $password
  2436. 2436      * @return bool
  2437. 2437      * @access private
  2438. 2438      */
  2439. 2439     function _keyboard_interactive_login($username$password)
  2440. 2440     {
  2441. 2441         $packet pack(
  2442. 2442             'CNa*Na*Na*Na*Na*',
  2443. 2443             NET_SSH2_MSG_USERAUTH_REQUEST,
  2444. 2444             strlen($username),
  2445. 2445             $username,
  2446. 2446             strlen('ssh-connection'),
  2447. 2447             'ssh-connection',
  2448. 2448             strlen('keyboard-interactive'),
  2449. 2449             'keyboard-interactive',
  2450. 2450             0,
  2451. 2451             '',
  2452. 2452             0,
  2453. 2453             ''
  2454. 2454         );
  2455. 2455 
  2456. 2456         if (!$this->_send_binary_packet($packet)) {
  2457. 2457             return false;
  2458. 2458         }
  2459. 2459 
  2460. 2460         return $this->_keyboard_interactive_process($password);
  2461. 2461     }
  2462. 2462 
  2463. 2463     /**
  2464. 2464      * Handle the keyboard-interactive requests / responses.
  2465. 2465      *
  2466. 2466      * @param string $responses...
  2467. 2467      * @return bool
  2468. 2468      * @access private
  2469. 2469      */
  2470. 2470     function _keyboard_interactive_process()
  2471. 2471     {
  2472. 2472         $responses func_get_args();
  2473. 2473 
  2474. 2474         if (strlen($this->last_interactive_response)) {
  2475. 2475             $response $this->last_interactive_response;
  2476. 2476         } else {
  2477. 2477             $orig $response $this->_get_binary_packet();
  2478. 2478             if ($response === false) {
  2479. 2479                 user_error('Connection closed by server');
  2480. 2480                 return false;
  2481. 2481             }
  2482. 2482         }
  2483. 2483 
  2484. 2484         if (!strlen($response)) {
  2485. 2485             return false;
  2486. 2486         }
  2487. 2487         extract(unpack('Ctype'$this->_string_shift($response1)));
  2488. 2488 
  2489. 2489         switch ($type) {
  2490. 2490             case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
  2491. 2491                 if (strlen($response) < 4) {
  2492. 2492                     return false;
  2493. 2493                 }
  2494. 2494                 extract(unpack('Nlength'$this->_string_shift($response4)));
  2495. 2495                 $this->_string_shift($response$length); // name; may be empty
  2496. 2496                 if (strlen($response) < 4) {
  2497. 2497                     return false;
  2498. 2498                 }
  2499. 2499                 extract(unpack('Nlength'$this->_string_shift($response4)));
  2500. 2500                 $this->_string_shift($response$length); // instruction; may be empty
  2501. 2501                 if (strlen($response) < 4) {
  2502. 2502                     return false;
  2503. 2503                 }
  2504. 2504                 extract(unpack('Nlength'$this->_string_shift($response4)));
  2505. 2505                 $this->_string_shift($response$length); // language tag; may be empty
  2506. 2506                 if (strlen($response) < 4) {
  2507. 2507                     return false;
  2508. 2508                 }
  2509. 2509                 extract(unpack('Nnum_prompts'$this->_string_shift($response4)));
  2510. 2510 
  2511. 2511                 for ($i 0$i count($responses); $i++) {
  2512. 2512                     if (is_array($responses[$i])) {
  2513. 2513                         foreach ($responses[$i] as $key => $value) {
  2514. 2514                             $this->keyboard_requests_responses[$key] = $value;
  2515. 2515                         }
  2516. 2516                         unset($responses[$i]);
  2517. 2517                     }
  2518. 2518                 }
  2519. 2519                 $responses array_values($responses);
  2520. 2520 
  2521. 2521                 if (isset($this->keyboard_requests_responses)) {
  2522. 2522                     for ($i 0$i $num_prompts$i++) {
  2523. 2523                         if (strlen($response) < 4) {
  2524. 2524                             return false;
  2525. 2525                         }
  2526. 2526                         extract(unpack('Nlength'$this->_string_shift($response4)));
  2527. 2527                         // prompt - ie. "Password: "; must not be empty
  2528. 2528                         $prompt $this->_string_shift($response$length);
  2529. 2529                         //$echo = $this->_string_shift($response) != chr(0);
  2530. 2530                         foreach ($this->keyboard_requests_responses as $key => $value) {
  2531. 2531                             if (substr($prompt0strlen($key)) == $key) {
  2532. 2532                                 $responses[] = $value;
  2533. 2533                                 break;
  2534. 2534                             }
  2535. 2535                         }
  2536. 2536                     }
  2537. 2537                 }
  2538. 2538 
  2539. 2539                 // see http://tools.ietf.org/html/rfc4256#section-3.2
  2540. 2540                 if (strlen($this->last_interactive_response)) {
  2541. 2541                     $this->last_interactive_response '';
  2542. 2542                 } elseif (defined('NET_SSH2_LOGGING')) {
  2543. 2543                     $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  2544. 2544                         'UNKNOWN',
  2545. 2545                         'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  2546. 2546                         $this->message_number_log[count($this->message_number_log) - 1]
  2547. 2547                     );
  2548. 2548                 }
  2549. 2549 
  2550. 2550                 if (!count($responses) && $num_prompts) {
  2551. 2551                     $this->last_interactive_response $orig;
  2552. 2552                     return false;
  2553. 2553                 }
  2554. 2554 
  2555. 2555                 /*
  2556. 2556                    After obtaining the requested information from the user, the client
  2557. 2557                    MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
  2558. 2558                 */
  2559. 2559                 // see http://tools.ietf.org/html/rfc4256#section-3.4
  2560. 2560                 $packet $logged pack('CN'NET_SSH2_MSG_USERAUTH_INFO_RESPONSEcount($responses));
  2561. 2561                 for ($i 0$i count($responses); $i++) {
  2562. 2562                     $packet.= pack('Na*'strlen($responses[$i]), $responses[$i]);
  2563. 2563                     $logged.= pack('Na*'strlen('dummy-answer'), 'dummy-answer');
  2564. 2564                 }
  2565. 2565 
  2566. 2566                 if (!$this->_send_binary_packet($packet$logged)) {
  2567. 2567                     return false;
  2568. 2568                 }
  2569. 2569 
  2570. 2570                 if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  2571. 2571                     $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  2572. 2572                         'UNKNOWN',
  2573. 2573                         'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
  2574. 2574                         $this->message_number_log[count($this->message_number_log) - 1]
  2575. 2575                     );
  2576. 2576                 }
  2577. 2577 
  2578. 2578                 /*
  2579. 2579                    After receiving the response, the server MUST send either an
  2580. 2580                    SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
  2581. 2581                    SSH_MSG_USERAUTH_INFO_REQUEST message.
  2582. 2582                 */
  2583. 2583                 // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
  2584. 2584                 // there could be an infinite loop of request / responses.
  2585. 2585                 return $this->_keyboard_interactive_process();
  2586. 2586             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2587. 2587                 return true;
  2588. 2588             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2589. 2589                 return false;
  2590. 2590         }
  2591. 2591 
  2592. 2592         return false;
  2593. 2593     }
  2594. 2594 
  2595. 2595     /**
  2596. 2596      * Login with an ssh-agent provided key
  2597. 2597      *
  2598. 2598      * @param string $username
  2599. 2599      * @param System_SSH_Agent $agent
  2600. 2600      * @return bool
  2601. 2601      * @access private
  2602. 2602      */
  2603. 2603     function _ssh_agent_login($username$agent)
  2604. 2604     {
  2605. 2605         $this->agent $agent;
  2606. 2606         $keys $agent->requestIdentities();
  2607. 2607         foreach ($keys as $key) {
  2608. 2608             if ($this->_privatekey_login($username$key)) {
  2609. 2609                 return true;
  2610. 2610             }
  2611. 2611         }
  2612. 2612 
  2613. 2613         return false;
  2614. 2614     }
  2615. 2615 
  2616. 2616     /**
  2617. 2617      * Login with an RSA private key
  2618. 2618      *
  2619. 2619      * @param string $username
  2620. 2620      * @param Crypt_RSA $password
  2621. 2621      * @return bool
  2622. 2622      * @access private
  2623. 2623      * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  2624. 2624      *           by sending dummy SSH_MSG_IGNORE messages.
  2625. 2625      */
  2626. 2626     function _privatekey_login($username$privatekey)
  2627. 2627     {
  2628. 2628         // see http://tools.ietf.org/html/rfc4253#page-15
  2629. 2629         $publickey $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  2630. 2630         if ($publickey === false) {
  2631. 2631             return false;
  2632. 2632         }
  2633. 2633 
  2634. 2634         $publickey = array(
  2635. 2635             'e' => $publickey['e']->toBytes(true),
  2636. 2636             'n' => $publickey['n']->toBytes(true)
  2637. 2637         );
  2638. 2638         $publickey pack(
  2639. 2639             'Na*Na*Na*',
  2640. 2640             strlen('ssh-rsa'),
  2641. 2641             'ssh-rsa',
  2642. 2642             strlen($publickey['e']),
  2643. 2643             $publickey['e'],
  2644. 2644             strlen($publickey['n']),
  2645. 2645             $publickey['n']
  2646. 2646         );
  2647. 2647 
  2648. 2648         $part1 pack(
  2649. 2649             'CNa*Na*Na*',
  2650. 2650             NET_SSH2_MSG_USERAUTH_REQUEST,
  2651. 2651             strlen($username),
  2652. 2652             $username,
  2653. 2653             strlen('ssh-connection'),
  2654. 2654             'ssh-connection',
  2655. 2655             strlen('publickey'),
  2656. 2656             'publickey'
  2657. 2657         );
  2658. 2658         $part2 pack('Na*Na*'strlen('ssh-rsa'), 'ssh-rsa'strlen($publickey), $publickey);
  2659. 2659 
  2660. 2660         $packet $part1 chr(0) . $part2;
  2661. 2661         if (!$this->_send_binary_packet($packet)) {
  2662. 2662             return false;
  2663. 2663         }
  2664. 2664 
  2665. 2665         $response $this->_get_binary_packet();
  2666. 2666         if ($response === false) {
  2667. 2667             user_error('Connection closed by server');
  2668. 2668             return false;
  2669. 2669         }
  2670. 2670 
  2671. 2671         if (!strlen($response)) {
  2672. 2672             return false;
  2673. 2673         }
  2674. 2674         extract(unpack('Ctype'$this->_string_shift($response1)));
  2675. 2675 
  2676. 2676         switch ($type) {
  2677. 2677             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2678. 2678                 if (strlen($response) < 4) {
  2679. 2679                     return false;
  2680. 2680                 }
  2681. 2681                 extract(unpack('Nlength'$this->_string_shift($response4)));
  2682. 2682                 $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' $this->_string_shift($response$length);
  2683. 2683                 return false;
  2684. 2684             case NET_SSH2_MSG_USERAUTH_PK_OK:
  2685. 2685                 // we'll just take it on faith that the public key blob and the public key algorithm name are as
  2686. 2686                 // they should be
  2687. 2687                 if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  2688. 2688                     $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  2689. 2689                         'UNKNOWN',
  2690. 2690                         'NET_SSH2_MSG_USERAUTH_PK_OK',
  2691. 2691                         $this->message_number_log[count($this->message_number_log) - 1]
  2692. 2692                     );
  2693. 2693                 }
  2694. 2694         }
  2695. 2695 
  2696. 2696         $packet $part1 chr(1) . $part2;
  2697. 2697         $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  2698. 2698         $signature $privatekey->sign(pack('Na*a*'strlen($this->session_id), $this->session_id$packet));
  2699. 2699         $signature pack('Na*Na*'strlen('ssh-rsa'), 'ssh-rsa'strlen($signature), $signature);
  2700. 2700         $packet.= pack('Na*'strlen($signature), $signature);
  2701. 2701 
  2702. 2702         if (!$this->_send_binary_packet($packet)) {
  2703. 2703             return false;
  2704. 2704         }
  2705. 2705 
  2706. 2706         $response $this->_get_binary_packet();
  2707. 2707         if ($response === false) {
  2708. 2708             user_error('Connection closed by server');
  2709. 2709             return false;
  2710. 2710         }
  2711. 2711 
  2712. 2712         if (!strlen($response)) {
  2713. 2713             return false;
  2714. 2714         }
  2715. 2715         extract(unpack('Ctype'$this->_string_shift($response1)));
  2716. 2716 
  2717. 2717         switch ($type) {
  2718. 2718             case NET_SSH2_MSG_USERAUTH_FAILURE:
  2719. 2719                 // either the login is bad or the server employs multi-factor authentication
  2720. 2720                 return false;
  2721. 2721             case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2722. 2722                 $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2723. 2723                 return true;
  2724. 2724         }
  2725. 2725 
  2726. 2726         return false;
  2727. 2727     }
  2728. 2728 
  2729. 2729     /**
  2730. 2730      * Set Timeout
  2731. 2731      *
  2732. 2732      * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely.  setTimeout() makes it so it'll timeout.
  2733. 2733      * Setting $timeout to false or 0 will mean there is no timeout.
  2734. 2734      *
  2735. 2735      * @param mixed $timeout
  2736. 2736      * @access public
  2737. 2737      */
  2738. 2738     function setTimeout($timeout)
  2739. 2739     {
  2740. 2740         $this->timeout $this->curTimeout $timeout;
  2741. 2741     }
  2742. 2742 
  2743. 2743     /**
  2744. 2744      * Get the output from stdError
  2745. 2745      *
  2746. 2746      * @access public
  2747. 2747      */
  2748. 2748     function getStdError()
  2749. 2749     {
  2750. 2750         return $this->stdErrorLog;
  2751. 2751     }
  2752. 2752 
  2753. 2753     /**
  2754. 2754      * Execute Command
  2755. 2755      *
  2756. 2756      * If $callback is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
  2757. 2757      * In all likelihood, this is not a feature you want to be taking advantage of.
  2758. 2758      *
  2759. 2759      * @param string $command
  2760. 2760      * @param Callback $callback
  2761. 2761      * @return string
  2762. 2762      * @access public
  2763. 2763      */
  2764. 2764     function exec($command$callback null)
  2765. 2765     {
  2766. 2766         $this->curTimeout $this->timeout;
  2767. 2767         $this->is_timeout false;
  2768. 2768         $this->stdErrorLog '';
  2769. 2769 
  2770. 2770         if (!$this->isAuthenticated()) {
  2771. 2771             return false;
  2772. 2772         }
  2773. 2773 
  2774. 2774         if ($this->in_request_pty_exec) {
  2775. 2775             user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
  2776. 2776             return false;
  2777. 2777         }
  2778. 2778 
  2779. 2779         // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
  2780. 2780         // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
  2781. 2781         // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
  2782. 2782         // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
  2783. 2783         $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC] = $this->window_size;
  2784. 2784         // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
  2785. 2785         // uses 0x4000, that's what will be used here, as well.
  2786. 2786         $packet_size 0x4000;
  2787. 2787 
  2788. 2788         $packet pack(
  2789. 2789             'CNa*N3',
  2790. 2790             NET_SSH2_MSG_CHANNEL_OPEN,
  2791. 2791             strlen('session'),
  2792. 2792             'session',
  2793. 2793             NET_SSH2_CHANNEL_EXEC,
  2794. 2794             $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC],
  2795. 2795             $packet_size
  2796. 2796         );
  2797. 2797 
  2798. 2798         if (!$this->_send_binary_packet($packet)) {
  2799. 2799             return false;
  2800. 2800         }
  2801. 2801 
  2802. 2802         $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
  2803. 2803 
  2804. 2804         $response $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  2805. 2805         if ($response === false) {
  2806. 2806             return false;
  2807. 2807         }
  2808. 2808 
  2809. 2809         if ($this->request_pty === true) {
  2810. 2810             $terminal_modes pack('C'NET_SSH2_TTY_OP_END);
  2811. 2811             $packet pack(
  2812. 2812                 'CNNa*CNa*N5a*',
  2813. 2813                 NET_SSH2_MSG_CHANNEL_REQUEST,
  2814. 2814                 $this->server_channels[NET_SSH2_CHANNEL_EXEC],
  2815. 2815                 strlen('pty-req'),
  2816. 2816                 'pty-req',
  2817. 2817                 1,
  2818. 2818                 strlen('vt100'),
  2819. 2819                 'vt100',
  2820. 2820                 $this->windowColumns,
  2821. 2821                 $this->windowRows,
  2822. 2822                 0,
  2823. 2823                 0,
  2824. 2824                 strlen($terminal_modes),
  2825. 2825                 $terminal_modes
  2826. 2826             );
  2827. 2827 
  2828. 2828             if (!$this->_send_binary_packet($packet)) {
  2829. 2829                 return false;
  2830. 2830             }
  2831. 2831 
  2832. 2832             $response $this->_get_binary_packet();
  2833. 2833             if ($response === false) {
  2834. 2834                 user_error('Connection closed by server');
  2835. 2835                 return false;
  2836. 2836             }
  2837. 2837 
  2838. 2838             if (!strlen($response)) {
  2839. 2839                 return false;
  2840. 2840             }
  2841. 2841             list(, $type) = unpack('C'$this->_string_shift($response1));
  2842. 2842 
  2843. 2843             switch ($type) {
  2844. 2844                 case NET_SSH2_MSG_CHANNEL_SUCCESS:
  2845. 2845                     break;
  2846. 2846                 case NET_SSH2_MSG_CHANNEL_FAILURE:
  2847. 2847                 default:
  2848. 2848                     user_error('Unable to request pseudo-terminal');
  2849. 2849                     return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2850. 2850             }
  2851. 2851             $this->in_request_pty_exec true;
  2852. 2852         }
  2853. 2853 
  2854. 2854         // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
  2855. 2855         // down.  the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
  2856. 2856         // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
  2857. 2857         // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
  2858. 2858         // neither will your script.
  2859. 2859 
  2860. 2860         // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
  2861. 2861         // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
  2862. 2862         // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
  2863. 2863         $packet pack(
  2864. 2864             'CNNa*CNa*',
  2865. 2865             NET_SSH2_MSG_CHANNEL_REQUEST,
  2866. 2866             $this->server_channels[NET_SSH2_CHANNEL_EXEC],
  2867. 2867             strlen('exec'),
  2868. 2868             'exec',
  2869. 2869             1,
  2870. 2870             strlen($command),
  2871. 2871             $command
  2872. 2872         );
  2873. 2873 
  2874. 2874         if (!$this->_send_binary_packet($packet)) {
  2875. 2875             return false;
  2876. 2876         }
  2877. 2877 
  2878. 2878         $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2879. 2879 
  2880. 2880         $response $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  2881. 2881         if ($response === false) {
  2882. 2882             return false;
  2883. 2883         }
  2884. 2884 
  2885. 2885         $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
  2886. 2886 
  2887. 2887         if ($callback === false || $this->in_request_pty_exec) {
  2888. 2888             return true;
  2889. 2889         }
  2890. 2890 
  2891. 2891         $output '';
  2892. 2892         while (true) {
  2893. 2893             $temp $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  2894. 2894             switch (true) {
  2895. 2895                 case $temp === true:
  2896. 2896                     return is_callable($callback) ? true $output;
  2897. 2897                 case $temp === false:
  2898. 2898                     return false;
  2899. 2899                 default:
  2900. 2900                     if (is_callable($callback)) {
  2901. 2901                         if (call_user_func($callback$temp) === true) {
  2902. 2902                             $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
  2903. 2903                             return true;
  2904. 2904                         }
  2905. 2905                     } else {
  2906. 2906                         $output.= $temp;
  2907. 2907                     }
  2908. 2908             }
  2909. 2909         }
  2910. 2910     }
  2911. 2911 
  2912. 2912     /**
  2913. 2913      * Creates an interactive shell
  2914. 2914      *
  2915. 2915      * @see self::read()
  2916. 2916      * @see self::write()
  2917. 2917      * @return bool
  2918. 2918      * @access private
  2919. 2919      */
  2920. 2920     function _initShell()
  2921. 2921     {
  2922. 2922         if ($this->in_request_pty_exec === true) {
  2923. 2923             return true;
  2924. 2924         }
  2925. 2925 
  2926. 2926         $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL] = $this->window_size;
  2927. 2927         $packet_size 0x4000;
  2928. 2928 
  2929. 2929         $packet pack(
  2930. 2930             'CNa*N3',
  2931. 2931             NET_SSH2_MSG_CHANNEL_OPEN,
  2932. 2932             strlen('session'),
  2933. 2933             'session',
  2934. 2934             NET_SSH2_CHANNEL_SHELL,
  2935. 2935             $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL],
  2936. 2936             $packet_size
  2937. 2937         );
  2938. 2938 
  2939. 2939         if (!$this->_send_binary_packet($packet)) {
  2940. 2940             return false;
  2941. 2941         }
  2942. 2942 
  2943. 2943         $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
  2944. 2944 
  2945. 2945         $response $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  2946. 2946         if ($response === false) {
  2947. 2947             return false;
  2948. 2948         }
  2949. 2949 
  2950. 2950         $terminal_modes pack('C'NET_SSH2_TTY_OP_END);
  2951. 2951         $packet pack(
  2952. 2952             'CNNa*CNa*N5a*',
  2953. 2953             NET_SSH2_MSG_CHANNEL_REQUEST,
  2954. 2954             $this->server_channels[NET_SSH2_CHANNEL_SHELL],
  2955. 2955             strlen('pty-req'),
  2956. 2956             'pty-req',
  2957. 2957             1,
  2958. 2958             strlen('vt100'),
  2959. 2959             'vt100',
  2960. 2960             $this->windowColumns,
  2961. 2961             $this->windowRows,
  2962. 2962             0,
  2963. 2963             0,
  2964. 2964             strlen($terminal_modes),
  2965. 2965             $terminal_modes
  2966. 2966         );
  2967. 2967 
  2968. 2968         if (!$this->_send_binary_packet($packet)) {
  2969. 2969             return false;
  2970. 2970         }
  2971. 2971 
  2972. 2972         $response $this->_get_binary_packet();
  2973. 2973         if ($response === false) {
  2974. 2974             user_error('Connection closed by server');
  2975. 2975             return false;
  2976. 2976         }
  2977. 2977 
  2978. 2978         if (!strlen($response)) {
  2979. 2979             return false;
  2980. 2980         }
  2981. 2981         list(, $type) = unpack('C'$this->_string_shift($response1));
  2982. 2982 
  2983. 2983         switch ($type) {
  2984. 2984             case NET_SSH2_MSG_CHANNEL_SUCCESS:
  2985. 2985             // if a pty can't be opened maybe commands can still be executed
  2986. 2986             case NET_SSH2_MSG_CHANNEL_FAILURE:
  2987. 2987                 break;
  2988. 2988             default:
  2989. 2989                 user_error('Unable to request pseudo-terminal');
  2990. 2990                 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2991. 2991         }
  2992. 2992 
  2993. 2993         $packet pack(
  2994. 2994             'CNNa*C',
  2995. 2995             NET_SSH2_MSG_CHANNEL_REQUEST,
  2996. 2996             $this->server_channels[NET_SSH2_CHANNEL_SHELL],
  2997. 2997             strlen('shell'),
  2998. 2998             'shell',
  2999. 2999             1
  3000. 3000         );
  3001. 3001         if (!$this->_send_binary_packet($packet)) {
  3002. 3002             return false;
  3003. 3003         }
  3004. 3004 
  3005. 3005         $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  3006. 3006 
  3007. 3007         $response $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  3008. 3008         if ($response === false) {
  3009. 3009             return false;
  3010. 3010         }
  3011. 3011 
  3012. 3012         $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
  3013. 3013 
  3014. 3014         $this->bitmap |= NET_SSH2_MASK_SHELL;
  3015. 3015 
  3016. 3016         return true;
  3017. 3017     }
  3018. 3018 
  3019. 3019     /**
  3020. 3020      * Return the channel to be used with read() / write()
  3021. 3021      *
  3022. 3022      * @see self::read()
  3023. 3023      * @see self::write()
  3024. 3024      * @return int
  3025. 3025      * @access public
  3026. 3026      */
  3027. 3027     function _get_interactive_channel()
  3028. 3028     {
  3029. 3029         switch (true) {
  3030. 3030             case $this->in_subsystem:
  3031. 3031                 return NET_SSH2_CHANNEL_SUBSYSTEM;
  3032. 3032             case $this->in_request_pty_exec:
  3033. 3033                 return NET_SSH2_CHANNEL_EXEC;
  3034. 3034             default:
  3035. 3035                 return NET_SSH2_CHANNEL_SHELL;
  3036. 3036         }
  3037. 3037     }
  3038. 3038 
  3039. 3039     /**
  3040. 3040      * Return an available open channel
  3041. 3041      *
  3042. 3042      * @return int
  3043. 3043      * @access public
  3044. 3044      */
  3045. 3045     function _get_open_channel()
  3046. 3046     {
  3047. 3047         $channel NET_SSH2_CHANNEL_EXEC;
  3048. 3048         do {
  3049. 3049             if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
  3050. 3050                 return $channel;
  3051. 3051             }
  3052. 3052         } while ($channel++ < NET_SSH2_CHANNEL_SUBSYSTEM);
  3053. 3053 
  3054. 3054         return false;
  3055. 3055     }
  3056. 3056 
  3057. 3057     /**
  3058. 3058      * Returns the output of an interactive shell
  3059. 3059      *
  3060. 3060      * Returns when there's a match for $expect, which can take the form of a string literal or,
  3061. 3061      * if $mode == NET_SSH2_READ_REGEX, a regular expression.
  3062. 3062      *
  3063. 3063      * @see self::write()
  3064. 3064      * @param string $expect
  3065. 3065      * @param int $mode
  3066. 3066      * @return string
  3067. 3067      * @access public
  3068. 3068      */
  3069. 3069     function read($expect ''$mode NET_SSH2_READ_SIMPLE)
  3070. 3070     {
  3071. 3071         $this->curTimeout $this->timeout;
  3072. 3072         $this->is_timeout false;
  3073. 3073 
  3074. 3074         if (!$this->isAuthenticated()) {
  3075. 3075             user_error('Operation disallowed prior to login()');
  3076. 3076             return false;
  3077. 3077         }
  3078. 3078 
  3079. 3079         if (!($this->bitmap NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  3080. 3080             user_error('Unable to initiate an interactive shell session');
  3081. 3081             return false;
  3082. 3082         }
  3083. 3083 
  3084. 3084         $channel $this->_get_interactive_channel();
  3085. 3085 
  3086. 3086         if ($mode == NET_SSH2_READ_NEXT) {
  3087. 3087             return $this->_get_channel_packet($channel);
  3088. 3088         }
  3089. 3089 
  3090. 3090         $match $expect;
  3091. 3091         while (true) {
  3092. 3092             if ($mode == NET_SSH2_READ_REGEX) {
  3093. 3093                 preg_match($expectsubstr($this->interactiveBuffer, -1024), $matches);
  3094. 3094                 $match = isset($matches[0]) ? $matches[0] : '';
  3095. 3095             }
  3096. 3096             $pos strlen($match) ? strpos($this->interactiveBuffer$match) : false;
  3097. 3097             if ($pos !== false) {
  3098. 3098                 return $this->_string_shift($this->interactiveBuffer$pos strlen($match));
  3099. 3099             }
  3100. 3100             $response $this->_get_channel_packet($channel);
  3101. 3101             if (is_bool($response)) {
  3102. 3102                 $this->in_request_pty_exec false;
  3103. 3103                 return $response $this->_string_shift($this->interactiveBufferstrlen($this->interactiveBuffer)) : false;
  3104. 3104             }
  3105. 3105 
  3106. 3106             $this->interactiveBuffer.= $response;
  3107. 3107         }
  3108. 3108     }
  3109. 3109 
  3110. 3110     /**
  3111. 3111      * Inputs a command into an interactive shell.
  3112. 3112      *
  3113. 3113      * @see self::read()
  3114. 3114      * @param string $cmd
  3115. 3115      * @return bool
  3116. 3116      * @access public
  3117. 3117      */
  3118. 3118     function write($cmd)
  3119. 3119     {
  3120. 3120         if (!$this->isAuthenticated()) {
  3121. 3121             user_error('Operation disallowed prior to login()');
  3122. 3122             return false;
  3123. 3123         }
  3124. 3124 
  3125. 3125         if (!($this->bitmap NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  3126. 3126             user_error('Unable to initiate an interactive shell session');
  3127. 3127             return false;
  3128. 3128         }
  3129. 3129 
  3130. 3130         return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
  3131. 3131     }
  3132. 3132 
  3133. 3133     /**
  3134. 3134      * Start a subsystem.
  3135. 3135      *
  3136. 3136      * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
  3137. 3137      * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
  3138. 3138      * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
  3139. 3139      * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
  3140. 3140      * if there's sufficient demand for such a feature.
  3141. 3141      *
  3142. 3142      * @see self::stopSubsystem()
  3143. 3143      * @param string $subsystem
  3144. 3144      * @return bool
  3145. 3145      * @access public
  3146. 3146      */
  3147. 3147     function startSubsystem($subsystem)
  3148. 3148     {
  3149. 3149         $this->window_size_server_to_client[NET_SSH2_CHANNEL_SUBSYSTEM] = $this->window_size;
  3150. 3150 
  3151. 3151         $packet pack(
  3152. 3152             'CNa*N3',
  3153. 3153             NET_SSH2_MSG_CHANNEL_OPEN,
  3154. 3154             strlen('session'),
  3155. 3155             'session',
  3156. 3156             NET_SSH2_CHANNEL_SUBSYSTEM,
  3157. 3157             $this->window_size,
  3158. 3158             0x4000
  3159. 3159         );
  3160. 3160 
  3161. 3161         if (!$this->_send_binary_packet($packet)) {
  3162. 3162             return false;
  3163. 3163         }
  3164. 3164 
  3165. 3165         $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
  3166. 3166 
  3167. 3167         $response $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
  3168. 3168         if ($response === false) {
  3169. 3169             return false;
  3170. 3170         }
  3171. 3171 
  3172. 3172         $packet pack(
  3173. 3173             'CNNa*CNa*',
  3174. 3174             NET_SSH2_MSG_CHANNEL_REQUEST,
  3175. 3175             $this->server_channels[NET_SSH2_CHANNEL_SUBSYSTEM],
  3176. 3176             strlen('subsystem'),
  3177. 3177             'subsystem',
  3178. 3178             1,
  3179. 3179             strlen($subsystem),
  3180. 3180             $subsystem
  3181. 3181         );
  3182. 3182         if (!$this->_send_binary_packet($packet)) {
  3183. 3183             return false;
  3184. 3184         }
  3185. 3185 
  3186. 3186         $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
  3187. 3187 
  3188. 3188         $response $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
  3189. 3189 
  3190. 3190         if ($response === false) {
  3191. 3191             return false;
  3192. 3192         }
  3193. 3193 
  3194. 3194         $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
  3195. 3195 
  3196. 3196         $this->bitmap |= NET_SSH2_MASK_SHELL;
  3197. 3197         $this->in_subsystem true;
  3198. 3198 
  3199. 3199         return true;
  3200. 3200     }
  3201. 3201 
  3202. 3202     /**
  3203. 3203      * Stops a subsystem.
  3204. 3204      *
  3205. 3205      * @see self::startSubsystem()
  3206. 3206      * @return bool
  3207. 3207      * @access public
  3208. 3208      */
  3209. 3209     function stopSubsystem()
  3210. 3210     {
  3211. 3211         $this->in_subsystem false;
  3212. 3212         $this->_close_channel(NET_SSH2_CHANNEL_SUBSYSTEM);
  3213. 3213         return true;
  3214. 3214     }
  3215. 3215 
  3216. 3216     /**
  3217. 3217      * Closes a channel
  3218. 3218      *
  3219. 3219      * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
  3220. 3220      *
  3221. 3221      * @access public
  3222. 3222      */
  3223. 3223     function reset()
  3224. 3224     {
  3225. 3225         $this->_close_channel($this->_get_interactive_channel());
  3226. 3226     }
  3227. 3227 
  3228. 3228     /**
  3229. 3229      * Is timeout?
  3230. 3230      *
  3231. 3231      * Did exec() or read() return because they timed out or because they encountered the end?
  3232. 3232      *
  3233. 3233      * @access public
  3234. 3234      */
  3235. 3235     function isTimeout()
  3236. 3236     {
  3237. 3237         return $this->is_timeout;
  3238. 3238     }
  3239. 3239 
  3240. 3240     /**
  3241. 3241      * Disconnect
  3242. 3242      *
  3243. 3243      * @access public
  3244. 3244      */
  3245. 3245     function disconnect()
  3246. 3246     {
  3247. 3247         $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3248. 3248         if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
  3249. 3249             fclose($this->realtime_log_file);
  3250. 3250         }
  3251. 3251     }
  3252. 3252 
  3253. 3253     /**
  3254. 3254      * Destructor.
  3255. 3255      *
  3256. 3256      * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
  3257. 3257      * disconnect().
  3258. 3258      *
  3259. 3259      * @access public
  3260. 3260      */
  3261. 3261     function __destruct()
  3262. 3262     {
  3263. 3263         $this->disconnect();
  3264. 3264     }
  3265. 3265 
  3266. 3266     /**
  3267. 3267      * Is the connection still active?
  3268. 3268      *
  3269. 3269      * @return bool
  3270. 3270      * @access public
  3271. 3271      */
  3272. 3272     function isConnected()
  3273. 3273     {
  3274. 3274         return (bool) ($this->bitmap NET_SSH2_MASK_CONNECTED);
  3275. 3275     }
  3276. 3276 
  3277. 3277     /**
  3278. 3278      * Have you successfully been logged in?
  3279. 3279      *
  3280. 3280      * @return bool
  3281. 3281      * @access public
  3282. 3282      */
  3283. 3283     function isAuthenticated()
  3284. 3284     {
  3285. 3285         return (bool) ($this->bitmap NET_SSH2_MASK_LOGIN);
  3286. 3286     }
  3287. 3287 
  3288. 3288     /**
  3289. 3289      * Resets a connection for re-use
  3290. 3290      *
  3291. 3291      * @param int $reason
  3292. 3292      * @access private
  3293. 3293      */
  3294. 3294     function _reset_connection($reason)
  3295. 3295     {
  3296. 3296         $this->_disconnect($reason);
  3297. 3297         $this->decrypt $this->encrypt false;
  3298. 3298         $this->decrypt_block_size $this->encrypt_block_size 8;
  3299. 3299         $this->hmac_check $this->hmac_create false;
  3300. 3300         $this->hmac_size false;
  3301. 3301         $this->session_id false;
  3302. 3302         $this->retry_connect true;
  3303. 3303         $this->get_seq_no $this->send_seq_no 0;
  3304. 3304     }
  3305. 3305 
  3306. 3306     /**
  3307. 3307      * Gets Binary Packets
  3308. 3308      *
  3309. 3309      * See '6. Binary Packet Protocol' of rfc4253 for more info.
  3310. 3310      *
  3311. 3311      * @see self::_send_binary_packet()
  3312. 3312      * @return string
  3313. 3313      * @access private
  3314. 3314      */
  3315. 3315     function _get_binary_packet($skip_channel_filter false)
  3316. 3316     {
  3317. 3317         if (!is_resource($this->fsock) || feof($this->fsock)) {
  3318. 3318             user_error('Connection closed prematurely');
  3319. 3319             $this->bitmap 0;
  3320. 3320             return false;
  3321. 3321         }
  3322. 3322 
  3323. 3323         $start strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  3324. 3324         $raw fread($this->fsock$this->decrypt_block_size);
  3325. 3325 
  3326. 3326         if (!strlen($raw)) {
  3327. 3327             return '';
  3328. 3328         }
  3329. 3329 
  3330. 3330         if ($this->decrypt !== false) {
  3331. 3331             $raw $this->decrypt->decrypt($raw);
  3332. 3332         }
  3333. 3333         if ($raw === false) {
  3334. 3334             user_error('Unable to decrypt content');
  3335. 3335             return false;
  3336. 3336         }
  3337. 3337 
  3338. 3338         if (strlen($raw) < 5) {
  3339. 3339             return false;
  3340. 3340         }
  3341. 3341         extract(unpack('Npacket_length/Cpadding_length'$this->_string_shift($raw5)));
  3342. 3342 
  3343. 3343         $remaining_length $packet_length $this->decrypt_block_size;
  3344. 3344 
  3345. 3345         // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
  3346. 3346         // "implementations SHOULD check that the packet length is reasonable"
  3347. 3347         // PuTTY uses 0x9000 as the actual max packet size and so to shall we
  3348. 3348         if ($remaining_length < -$this->decrypt_block_size || $remaining_length 0x9000 || $remaining_length $this->decrypt_block_size != 0) {
  3349. 3349             if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt_algorithm) && !($this->bitmap NET_SSH2_MASK_LOGIN)) {
  3350. 3350                 $this->bad_key_size_fix true;
  3351. 3351                 $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  3352. 3352                 return false;
  3353. 3353             }
  3354. 3354             user_error('Invalid size');
  3355. 3355             return false;
  3356. 3356         }
  3357. 3357 
  3358. 3358         $buffer '';
  3359. 3359         while ($remaining_length 0) {
  3360. 3360             $temp fread($this->fsock$remaining_length);
  3361. 3361             if ($temp === false || feof($this->fsock)) {
  3362. 3362                 user_error('Error reading from socket');
  3363. 3363                 $this->bitmap 0;
  3364. 3364                 return false;
  3365. 3365             }
  3366. 3366             $buffer.= $temp;
  3367. 3367             $remaining_length-= strlen($temp);
  3368. 3368         }
  3369. 3369 
  3370. 3370         $stop strtok(microtime(), ' ') + strtok('');
  3371. 3371         if (strlen($buffer)) {
  3372. 3372             $raw.= $this->decrypt !== false $this->decrypt->decrypt($buffer) : $buffer;
  3373. 3373         }
  3374. 3374 
  3375. 3375         $payload $this->_string_shift($raw$packet_length $padding_length 1);
  3376. 3376         $padding $this->_string_shift($raw$padding_length); // should leave $raw empty
  3377. 3377 
  3378. 3378         if ($this->hmac_check !== false) {
  3379. 3379             $hmac fread($this->fsock$this->hmac_size);
  3380. 3380             if ($hmac === false || strlen($hmac) != $this->hmac_size) {
  3381. 3381                 user_error('Error reading socket');
  3382. 3382                 $this->bitmap 0;
  3383. 3383                 return false;
  3384. 3384             } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*'$this->get_seq_no$packet_length$padding_length$payload $padding))) {
  3385. 3385                 user_error('Invalid HMAC');
  3386. 3386                 return false;
  3387. 3387             }
  3388. 3388         }
  3389. 3389 
  3390. 3390         //if ($this->decompress) {
  3391. 3391         //    $payload = gzinflate(substr($payload, 2));
  3392. 3392         //}
  3393. 3393 
  3394. 3394         $this->get_seq_no++;
  3395. 3395 
  3396. 3396         if (defined('NET_SSH2_LOGGING')) {
  3397. 3397             $current strtok(microtime(), ' ') + strtok('');
  3398. 3398             $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' ord($payload[0]) . ')';
  3399. 3399             $message_number '<- ' $message_number .
  3400. 3400                               ' (since last: ' round($current $this->last_packet4) . ', network: ' round($stop $start4) . 's)';
  3401. 3401             $this->_append_log($message_number$payload);
  3402. 3402             $this->last_packet $current;
  3403. 3403         }
  3404. 3404 
  3405. 3405         return $this->_filter($payload$skip_channel_filter);
  3406. 3406     }
  3407. 3407 
  3408. 3408     /**
  3409. 3409      * Filter Binary Packets
  3410. 3410      *
  3411. 3411      * Because some binary packets need to be ignored...
  3412. 3412      *
  3413. 3413      * @see self::_get_binary_packet()
  3414. 3414      * @return string
  3415. 3415      * @access private
  3416. 3416      */
  3417. 3417     function _filter($payload$skip_channel_filter)
  3418. 3418     {
  3419. 3419         switch (ord($payload[0])) {
  3420. 3420             case NET_SSH2_MSG_DISCONNECT:
  3421. 3421                 $this->_string_shift($payload1);
  3422. 3422                 if (strlen($payload) < 8) {
  3423. 3423                     return false;
  3424. 3424                 }
  3425. 3425                 extract(unpack('Nreason_code/Nlength'$this->_string_shift($payload8)));
  3426. 3426                 $this->errors[] = 'SSH_MSG_DISCONNECT: ' $this->disconnect_reasons[$reason_code] . "\r\n" utf8_decode($this->_string_shift($payload$length));
  3427. 3427                 $this->bitmap 0;
  3428. 3428                 return false;
  3429. 3429             case NET_SSH2_MSG_IGNORE:
  3430. 3430                 $payload $this->_get_binary_packet($skip_channel_filter);
  3431. 3431                 break;
  3432. 3432             case NET_SSH2_MSG_DEBUG:
  3433. 3433                 $this->_string_shift($payload2);
  3434. 3434                 if (strlen($payload) < 4) {
  3435. 3435                     return false;
  3436. 3436                 }
  3437. 3437                 extract(unpack('Nlength'$this->_string_shift($payload4)));
  3438. 3438                 $this->errors[] = 'SSH_MSG_DEBUG: ' utf8_decode($this->_string_shift($payload$length));
  3439. 3439                 $payload $this->_get_binary_packet($skip_channel_filter);
  3440. 3440                 break;
  3441. 3441             case NET_SSH2_MSG_UNIMPLEMENTED:
  3442. 3442                 return false;
  3443. 3443             case NET_SSH2_MSG_KEXINIT:
  3444. 3444                 if ($this->session_id !== false) {
  3445. 3445                     $this->send_kex_first false;
  3446. 3446                     if (!$this->_key_exchange($payload)) {
  3447. 3447                         $this->bitmap 0;
  3448. 3448                         return false;
  3449. 3449                     }
  3450. 3450                     $payload $this->_get_binary_packet($skip_channel_filter);
  3451. 3451                 }
  3452. 3452         }
  3453. 3453 
  3454. 3454         // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
  3455. 3455         if (($this->bitmap NET_SSH2_MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
  3456. 3456             $this->_string_shift($payload1);
  3457. 3457             if (strlen($payload) < 4) {
  3458. 3458                 return false;
  3459. 3459             }
  3460. 3460             extract(unpack('Nlength'$this->_string_shift($payload4)));
  3461. 3461             $this->banner_message utf8_decode($this->_string_shift($payload$length));
  3462. 3462             $payload $this->_get_binary_packet();
  3463. 3463         }
  3464. 3464 
  3465. 3465         // only called when we've already logged in
  3466. 3466         if (($this->bitmap NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) {
  3467. 3467             switch (ord($payload[0])) {
  3468. 3468                 case NET_SSH2_MSG_CHANNEL_DATA:
  3469. 3469                 case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  3470. 3470                 case NET_SSH2_MSG_CHANNEL_REQUEST:
  3471. 3471                 case NET_SSH2_MSG_CHANNEL_CLOSE:
  3472. 3472                 case NET_SSH2_MSG_CHANNEL_EOF:
  3473. 3473                     if (!$skip_channel_filter && !empty($this->server_channels)) {
  3474. 3474                         $this->binary_packet_buffer $payload;
  3475. 3475                         $this->_get_channel_packet(true);
  3476. 3476                         $payload $this->_get_binary_packet();
  3477. 3477                     }
  3478. 3478                     break;
  3479. 3479                 case NET_SSH2_MSG_GLOBAL_REQUEST// see http://tools.ietf.org/html/rfc4254#section-4
  3480. 3480                     if (strlen($payload) < 4) {
  3481. 3481                         return false;
  3482. 3482                     }
  3483. 3483                     extract(unpack('Nlength'$this->_string_shift($payload4)));
  3484. 3484                     $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' $this->_string_shift($payload$length);
  3485. 3485 
  3486. 3486                     if (!$this->_send_binary_packet(pack('C'NET_SSH2_MSG_REQUEST_FAILURE))) {
  3487. 3487                         return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3488. 3488                     }
  3489. 3489 
  3490. 3490                     $payload $this->_get_binary_packet($skip_channel_filter);
  3491. 3491                     break;
  3492. 3492                 case NET_SSH2_MSG_CHANNEL_OPEN// see http://tools.ietf.org/html/rfc4254#section-5.1
  3493. 3493                     $this->_string_shift($payload1);
  3494. 3494                     if (strlen($payload) < 4) {
  3495. 3495                         return false;
  3496. 3496                     }
  3497. 3497                     extract(unpack('Nlength'$this->_string_shift($payload4)));
  3498. 3498                     $data $this->_string_shift($payload$length);
  3499. 3499                     if (strlen($payload) < 4) {
  3500. 3500                         return false;
  3501. 3501                     }
  3502. 3502                     extract(unpack('Nserver_channel'$this->_string_shift($payload4)));
  3503. 3503                     switch ($data) {
  3504. 3504                         case 'auth-agent':
  3505. 3505                         case 'auth-agent@openssh.com':
  3506. 3506                             if (isset($this->agent)) {
  3507. 3507                                 $new_channel NET_SSH2_CHANNEL_AGENT_FORWARD;
  3508. 3508 
  3509. 3509                                 if (strlen($payload) < 8) {
  3510. 3510                                     return false;
  3511. 3511                                 }
  3512. 3512                                 extract(unpack('Nremote_window_size'$this->_string_shift($payload4)));
  3513. 3513                                 extract(unpack('Nremote_maximum_packet_size'$this->_string_shift($payload4)));
  3514. 3514 
  3515. 3515                                 $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
  3516. 3516                                 $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
  3517. 3517                                 $this->window_size_client_to_server[$new_channel] = $this->window_size;
  3518. 3518 
  3519. 3519                                 $packet_size 0x4000;
  3520. 3520 
  3521. 3521                                 $packet pack(
  3522. 3522                                     'CN4',
  3523. 3523                                     NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
  3524. 3524                                     $server_channel,
  3525. 3525                                     $new_channel,
  3526. 3526                                     $packet_size,
  3527. 3527                                     $packet_size
  3528. 3528                                 );
  3529. 3529 
  3530. 3530                                 $this->server_channels[$new_channel] = $server_channel;
  3531. 3531                                 $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
  3532. 3532                                 if (!$this->_send_binary_packet($packet)) {
  3533. 3533                                     return false;
  3534. 3534                                 }
  3535. 3535                             }
  3536. 3536                             break;
  3537. 3537                         default:
  3538. 3538                             $packet pack(
  3539. 3539                                 'CN3a*Na*',
  3540. 3540                                 NET_SSH2_MSG_REQUEST_FAILURE,
  3541. 3541                                 $server_channel,
  3542. 3542                                 NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
  3543. 3543                                 0,
  3544. 3544                                 '',
  3545. 3545                                 0,
  3546. 3546                                 ''
  3547. 3547                             );
  3548. 3548 
  3549. 3549                             if (!$this->_send_binary_packet($packet)) {
  3550. 3550                                 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3551. 3551                             }
  3552. 3552                     }
  3553. 3553                     $payload $this->_get_binary_packet($skip_channel_filter);
  3554. 3554                     break;
  3555. 3555                 case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
  3556. 3556                     $this->_string_shift($payload1);
  3557. 3557                     if (strlen($payload) < 8) {
  3558. 3558                         return false;
  3559. 3559                     }
  3560. 3560                     extract(unpack('Nchannel'$this->_string_shift($payload4)));
  3561. 3561                     extract(unpack('Nwindow_size'$this->_string_shift($payload4)));
  3562. 3562                     $this->window_size_client_to_server[$channel]+= $window_size;
  3563. 3563 
  3564. 3564                     $payload = ($this->bitmap NET_SSH2_MASK_WINDOW_ADJUST) ? true $this->_get_binary_packet($skip_channel_filter);
  3565. 3565             }
  3566. 3566         }
  3567. 3567 
  3568. 3568         return $payload;
  3569. 3569     }
  3570. 3570 
  3571. 3571     /**
  3572. 3572      * Enable Quiet Mode
  3573. 3573      *
  3574. 3574      * Suppress stderr from output
  3575. 3575      *
  3576. 3576      * @access public
  3577. 3577      */
  3578. 3578     function enableQuietMode()
  3579. 3579     {
  3580. 3580         $this->quiet_mode true;
  3581. 3581     }
  3582. 3582 
  3583. 3583     /**
  3584. 3584      * Disable Quiet Mode
  3585. 3585      *
  3586. 3586      * Show stderr in output
  3587. 3587      *
  3588. 3588      * @access public
  3589. 3589      */
  3590. 3590     function disableQuietMode()
  3591. 3591     {
  3592. 3592         $this->quiet_mode false;
  3593. 3593     }
  3594. 3594 
  3595. 3595     /**
  3596. 3596      * Returns whether Quiet Mode is enabled or not
  3597. 3597      *
  3598. 3598      * @see self::enableQuietMode()
  3599. 3599      * @see self::disableQuietMode()
  3600. 3600      *
  3601. 3601      * @access public
  3602. 3602      * @return bool
  3603. 3603      */
  3604. 3604     function isQuietModeEnabled()
  3605. 3605     {
  3606. 3606         return $this->quiet_mode;
  3607. 3607     }
  3608. 3608 
  3609. 3609     /**
  3610. 3610      * Enable request-pty when using exec()
  3611. 3611      *
  3612. 3612      * @access public
  3613. 3613      */
  3614. 3614     function enablePTY()
  3615. 3615     {
  3616. 3616         $this->request_pty true;
  3617. 3617     }
  3618. 3618 
  3619. 3619     /**
  3620. 3620      * Disable request-pty when using exec()
  3621. 3621      *
  3622. 3622      * @access public
  3623. 3623      */
  3624. 3624     function disablePTY()
  3625. 3625     {
  3626. 3626         if ($this->in_request_pty_exec) {
  3627. 3627             $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
  3628. 3628             $this->in_request_pty_exec false;
  3629. 3629         }
  3630. 3630         $this->request_pty false;
  3631. 3631     }
  3632. 3632 
  3633. 3633     /**
  3634. 3634      * Returns whether request-pty is enabled or not
  3635. 3635      *
  3636. 3636      * @see self::enablePTY()
  3637. 3637      * @see self::disablePTY()
  3638. 3638      *
  3639. 3639      * @access public
  3640. 3640      * @return bool
  3641. 3641      */
  3642. 3642     function isPTYEnabled()
  3643. 3643     {
  3644. 3644         return $this->request_pty;
  3645. 3645     }
  3646. 3646 
  3647. 3647     /**
  3648. 3648      * Gets channel data
  3649. 3649      *
  3650. 3650      * Returns the data as a string if it's available and false if not.
  3651. 3651      *
  3652. 3652      * @param $client_channel
  3653. 3653      * @return mixed
  3654. 3654      * @access private
  3655. 3655      */
  3656. 3656     function _get_channel_packet($client_channel$skip_extended false)
  3657. 3657     {
  3658. 3658         if (!empty($this->channel_buffers[$client_channel])) {
  3659. 3659             return array_shift($this->channel_buffers[$client_channel]);
  3660. 3660         }
  3661. 3661 
  3662. 3662         while (true) {
  3663. 3663             if ($this->binary_packet_buffer !== false) {
  3664. 3664                 $response $this->binary_packet_buffer;
  3665. 3665                 $this->binary_packet_buffer false;
  3666. 3666             } else {
  3667. 3667                 if ($this->curTimeout) {
  3668. 3668                     if ($this->curTimeout 0) {
  3669. 3669                         $this->is_timeout true;
  3670. 3670                         return true;
  3671. 3671                     }
  3672. 3672 
  3673. 3673                     $read = array($this->fsock);
  3674. 3674                     $write $except null;
  3675. 3675 
  3676. 3676                     $start strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  3677. 3677                     $sec floor($this->curTimeout);
  3678. 3678                     $usec 1000000 * ($this->curTimeout $sec);
  3679. 3679                     // on windows this returns a "Warning: Invalid CRT parameters detected" error
  3680. 3680                     if (!@stream_select($read$write$except$sec$usec) && !count($read)) {
  3681. 3681                         $this->is_timeout true;
  3682. 3682                         return true;
  3683. 3683                     }
  3684. 3684                     $elapsed strtok(microtime(), ' ') + strtok('') - $start;
  3685. 3685                     $this->curTimeout-= $elapsed;
  3686. 3686                 }
  3687. 3687 
  3688. 3688                 $response $this->_get_binary_packet(true);
  3689. 3689                 if ($response === false) {
  3690. 3690                     user_error('Connection closed by server');
  3691. 3691                     return false;
  3692. 3692                 }
  3693. 3693             }
  3694. 3694 
  3695. 3695             if ($client_channel == -&& $response === true) {
  3696. 3696                 return true;
  3697. 3697             }
  3698. 3698             if (!strlen($response)) {
  3699. 3699                 return '';
  3700. 3700             }
  3701. 3701 
  3702. 3702             if (!strlen($response)) {
  3703. 3703                 return false;
  3704. 3704             }
  3705. 3705             extract(unpack('Ctype'$this->_string_shift($response1)));
  3706. 3706 
  3707. 3707             if (strlen($response) < 4) {
  3708. 3708                 return false;
  3709. 3709             }
  3710. 3710             if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
  3711. 3711                 extract(unpack('Nlength'$this->_string_shift($response4)));
  3712. 3712             } else {
  3713. 3713                 extract(unpack('Nchannel'$this->_string_shift($response4)));
  3714. 3714             }
  3715. 3715 
  3716. 3716             // will not be setup yet on incoming channel open request
  3717. 3717             if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
  3718. 3718                 $this->window_size_server_to_client[$channel]-= strlen($response);
  3719. 3719 
  3720. 3720                 // resize the window, if appropriate
  3721. 3721                 if ($this->window_size_server_to_client[$channel] < 0) {
  3722. 3722                     $packet pack('CNN'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST$this->server_channels[$channel], $this->window_size);
  3723. 3723                     if (!$this->_send_binary_packet($packet)) {
  3724. 3724                         return false;
  3725. 3725                     }
  3726. 3726                     $this->window_size_server_to_client[$channel]+= $this->window_size;
  3727. 3727                 }
  3728. 3728 
  3729. 3729                 switch ($type) {
  3730. 3730                     case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  3731. 3731                         /*
  3732. 3732                         if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
  3733. 3733                             $this->_send_channel_packet($client_channel, chr(0));
  3734. 3734                         }
  3735. 3735                         */
  3736. 3736                         // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
  3737. 3737                         if (strlen($response) < 8) {
  3738. 3738                             return false;
  3739. 3739                         }
  3740. 3740                         extract(unpack('Ndata_type_code/Nlength'$this->_string_shift($response8)));
  3741. 3741                         $data $this->_string_shift($response$length);
  3742. 3742                         $this->stdErrorLog.= $data;
  3743. 3743                         if ($skip_extended || $this->quiet_mode) {
  3744. 3744                             continue 2;
  3745. 3745                         }
  3746. 3746                         if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
  3747. 3747                             return $data;
  3748. 3748                         }
  3749. 3749                         if (!isset($this->channel_buffers[$channel])) {
  3750. 3750                             $this->channel_buffers[$channel] = array();
  3751. 3751                         }
  3752. 3752                         $this->channel_buffers[$channel][] = $data;
  3753. 3753 
  3754. 3754                         continue 2;
  3755. 3755                     case NET_SSH2_MSG_CHANNEL_REQUEST:
  3756. 3756                         if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
  3757. 3757                             continue 2;
  3758. 3758                         }
  3759. 3759                         if (strlen($response) < 4) {
  3760. 3760                             return false;
  3761. 3761                         }
  3762. 3762                         extract(unpack('Nlength'$this->_string_shift($response4)));
  3763. 3763                         $value $this->_string_shift($response$length);
  3764. 3764                         switch ($value) {
  3765. 3765                             case 'exit-signal':
  3766. 3766                                 $this->_string_shift($response1);
  3767. 3767                                 if (strlen($response) < 4) {
  3768. 3768                                     return false;
  3769. 3769                                 }
  3770. 3770                                 extract(unpack('Nlength'$this->_string_shift($response4)));
  3771. 3771                                 $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' $this->_string_shift($response$length);
  3772. 3772                                 $this->_string_shift($response1);
  3773. 3773                                 if (strlen($response) < 4) {
  3774. 3774                                     return false;
  3775. 3775                                 }
  3776. 3776                                 extract(unpack('Nlength'$this->_string_shift($response4)));
  3777. 3777                                 if ($length) {
  3778. 3778                                     $this->errors[count($this->errors)].= "\r\n" $this->_string_shift($response$length);
  3779. 3779                                 }
  3780. 3780 
  3781. 3781                                 $this->_send_binary_packet(pack('CN'NET_SSH2_MSG_CHANNEL_EOF$this->server_channels[$client_channel]));
  3782. 3782                                 $this->_send_binary_packet(pack('CN'NET_SSH2_MSG_CHANNEL_CLOSE$this->server_channels[$channel]));
  3783. 3783 
  3784. 3784                                 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
  3785. 3785 
  3786. 3786                                 continue 3;
  3787. 3787                             case 'exit-status':
  3788. 3788                                 if (strlen($response) < 5) {
  3789. 3789                                     return false;
  3790. 3790                                 }
  3791. 3791                                 extract(unpack('Cfalse/Nexit_status'$this->_string_shift($response5)));
  3792. 3792                                 $this->exit_status $exit_status;
  3793. 3793 
  3794. 3794                                 // "The client MAY ignore these messages."
  3795. 3795                                 // -- http://tools.ietf.org/html/rfc4254#section-6.10
  3796. 3796 
  3797. 3797                                 continue 3;
  3798. 3798                             default:
  3799. 3799                                 // "Some systems may not implement signals, in which case they SHOULD ignore this message."
  3800. 3800                                 //  -- http://tools.ietf.org/html/rfc4254#section-6.9
  3801. 3801                                 continue 3;
  3802. 3802                         }
  3803. 3803                 }
  3804. 3804 
  3805. 3805                 switch ($this->channel_status[$channel]) {
  3806. 3806                     case NET_SSH2_MSG_CHANNEL_OPEN:
  3807. 3807                         switch ($type) {
  3808. 3808                             case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
  3809. 3809                                 if (strlen($response) < 4) {
  3810. 3810                                     return false;
  3811. 3811                                 }
  3812. 3812                                 extract(unpack('Nserver_channel'$this->_string_shift($response4)));
  3813. 3813                                 $this->server_channels[$channel] = $server_channel;
  3814. 3814                                 if (strlen($response) < 4) {
  3815. 3815                                     return false;
  3816. 3816                                 }
  3817. 3817                                 extract(unpack('Nwindow_size'$this->_string_shift($response4)));
  3818. 3818                                 if ($window_size 0) {
  3819. 3819                                     $window_size&= 0x7FFFFFFF;
  3820. 3820                                     $window_size+= 0x80000000;
  3821. 3821                                 }
  3822. 3822                                 $this->window_size_client_to_server[$channel] = $window_size;
  3823. 3823                                 if (strlen($response) < 4) {
  3824. 3824                                      return false;
  3825. 3825                                 }
  3826. 3826                                 $temp unpack('Npacket_size_client_to_server'$this->_string_shift($response4));
  3827. 3827                                 $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
  3828. 3828                                 $result $client_channel == $channel true $this->_get_channel_packet($client_channel$skip_extended);
  3829. 3829                                 $this->_on_channel_open();
  3830. 3830                                 return $result;
  3831. 3831                             //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
  3832. 3832                             default:
  3833. 3833                                 user_error('Unable to open channel');
  3834. 3834                                 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3835. 3835                         }
  3836. 3836                         break;
  3837. 3837                     case NET_SSH2_MSG_CHANNEL_REQUEST:
  3838. 3838                         switch ($type) {
  3839. 3839                             case NET_SSH2_MSG_CHANNEL_SUCCESS:
  3840. 3840                                 return true;
  3841. 3841                             case NET_SSH2_MSG_CHANNEL_FAILURE:
  3842. 3842                                 return false;
  3843. 3843                             default:
  3844. 3844                                 user_error('Unable to fulfill channel request');
  3845. 3845                                 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3846. 3846                         }
  3847. 3847                     case NET_SSH2_MSG_CHANNEL_CLOSE:
  3848. 3848                         return $type == NET_SSH2_MSG_CHANNEL_CLOSE true $this->_get_channel_packet($client_channel$skip_extended);
  3849. 3849                 }
  3850. 3850             }
  3851. 3851 
  3852. 3852             // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
  3853. 3853 
  3854. 3854             switch ($type) {
  3855. 3855                 case NET_SSH2_MSG_CHANNEL_DATA:
  3856. 3856                     /*
  3857. 3857                     if ($channel == NET_SSH2_CHANNEL_EXEC) {
  3858. 3858                         // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
  3859. 3859                         // this actually seems to make things twice as fast.  more to the point, the message right after
  3860. 3860                         // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
  3861. 3861                         // in OpenSSH it slows things down but only by a couple thousandths of a second.
  3862. 3862                         $this->_send_channel_packet($channel, chr(0));
  3863. 3863                     }
  3864. 3864                     */
  3865. 3865                     if (strlen($response) < 4) {
  3866. 3866                         return false;
  3867. 3867                     }
  3868. 3868                     extract(unpack('Nlength'$this->_string_shift($response4)));
  3869. 3869                     $data $this->_string_shift($response$length);
  3870. 3870 
  3871. 3871                     if ($channel == NET_SSH2_CHANNEL_AGENT_FORWARD) {
  3872. 3872                         $agent_response $this->agent->_forward_data($data);
  3873. 3873                         if (!is_bool($agent_response)) {
  3874. 3874                             $this->_send_channel_packet($channel$agent_response);
  3875. 3875                         }
  3876. 3876                         break;
  3877. 3877                     }
  3878. 3878 
  3879. 3879                     if ($client_channel == $channel) {
  3880. 3880                         return $data;
  3881. 3881                     }
  3882. 3882                     if (!isset($this->channel_buffers[$channel])) {
  3883. 3883                         $this->channel_buffers[$channel] = array();
  3884. 3884                     }
  3885. 3885                     $this->channel_buffers[$channel][] = $data;
  3886. 3886                     break;
  3887. 3887                 case NET_SSH2_MSG_CHANNEL_CLOSE:
  3888. 3888                     $this->curTimeout 0;
  3889. 3889 
  3890. 3890                     if ($this->bitmap NET_SSH2_MASK_SHELL) {
  3891. 3891                         $this->bitmap&= ~NET_SSH2_MASK_SHELL;
  3892. 3892                     }
  3893. 3893                     if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
  3894. 3894                         $this->_send_binary_packet(pack('CN'NET_SSH2_MSG_CHANNEL_CLOSE$this->server_channels[$channel]));
  3895. 3895                     }
  3896. 3896 
  3897. 3897                     $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  3898. 3898                     if ($client_channel == $channel) {
  3899. 3899                         return true;
  3900. 3900                     }
  3901. 3901                 case NET_SSH2_MSG_CHANNEL_EOF:
  3902. 3902                     break;
  3903. 3903                 default:
  3904. 3904                     user_error('Error reading channel data');
  3905. 3905                     return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3906. 3906             }
  3907. 3907         }
  3908. 3908     }
  3909. 3909 
  3910. 3910     /**
  3911. 3911      * Sends Binary Packets
  3912. 3912      *
  3913. 3913      * See '6. Binary Packet Protocol' of rfc4253 for more info.
  3914. 3914      *
  3915. 3915      * @param string $data
  3916. 3916      * @param string $logged
  3917. 3917      * @see self::_get_binary_packet()
  3918. 3918      * @return bool
  3919. 3919      * @access private
  3920. 3920      */
  3921. 3921     function _send_binary_packet($data$logged null)
  3922. 3922     {
  3923. 3923         if (!is_resource($this->fsock) || feof($this->fsock)) {
  3924. 3924             user_error('Connection closed prematurely');
  3925. 3925             $this->bitmap 0;
  3926. 3926             return false;
  3927. 3927         }
  3928. 3928 
  3929. 3929         //if ($this->compress) {
  3930. 3930         //    // the -4 removes the checksum:
  3931. 3931         //    // http://php.net/function.gzcompress#57710
  3932. 3932         //    $data = substr(gzcompress($data), 0, -4);
  3933. 3933         //}
  3934. 3934 
  3935. 3935         // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
  3936. 3936         $packet_length strlen($data) + 9;
  3937. 3937         // round up to the nearest $this->encrypt_block_size
  3938. 3938         $packet_length+= (($this->encrypt_block_size 1) * $packet_length) % $this->encrypt_block_size;
  3939. 3939         // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
  3940. 3940         $padding_length $packet_length strlen($data) - 5;
  3941. 3941         $padding crypt_random_string($padding_length);
  3942. 3942 
  3943. 3943         // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
  3944. 3944         $packet pack('NCa*'$packet_length 4$padding_length$data $padding);
  3945. 3945 
  3946. 3946         $hmac $this->hmac_create !== false $this->hmac_create->hash(pack('Na*'$this->send_seq_no$packet)) : '';
  3947. 3947         $this->send_seq_no++;
  3948. 3948 
  3949. 3949         if ($this->encrypt !== false) {
  3950. 3950             $packet $this->encrypt->encrypt($packet);
  3951. 3951         }
  3952. 3952 
  3953. 3953         $packet.= $hmac;
  3954. 3954 
  3955. 3955         $start strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  3956. 3956         $result strlen($packet) == fputs($this->fsock$packet);
  3957. 3957         $stop strtok(microtime(), ' ') + strtok('');
  3958. 3958 
  3959. 3959         if (defined('NET_SSH2_LOGGING')) {
  3960. 3960             $current strtok(microtime(), ' ') + strtok('');
  3961. 3961             $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' ord($data[0]) . ')';
  3962. 3962             $message_number '-> ' $message_number .
  3963. 3963                               ' (since last: ' round($current $this->last_packet4) . ', network: ' round($stop $start4) . 's)';
  3964. 3964             $this->_append_log($message_number, isset($logged) ? $logged $data);
  3965. 3965             $this->last_packet $current;
  3966. 3966         }
  3967. 3967 
  3968. 3968         return $result;
  3969. 3969     }
  3970. 3970 
  3971. 3971     /**
  3972. 3972      * Logs data packets
  3973. 3973      *
  3974. 3974      * Makes sure that only the last 1MB worth of packets will be logged
  3975. 3975      *
  3976. 3976      * @param string $data
  3977. 3977      * @access private
  3978. 3978      */
  3979. 3979     function _append_log($message_number$message)
  3980. 3980     {
  3981. 3981         // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
  3982. 3982         if (strlen($message_number) > 2) {
  3983. 3983             $this->_string_shift($message);
  3984. 3984         }
  3985. 3985 
  3986. 3986         switch (NET_SSH2_LOGGING) {
  3987. 3987             // useful for benchmarks
  3988. 3988             case NET_SSH2_LOG_SIMPLE:
  3989. 3989                 $this->message_number_log[] = $message_number;
  3990. 3990                 break;
  3991. 3991             // the most useful log for SSH2
  3992. 3992             case NET_SSH2_LOG_COMPLEX:
  3993. 3993                 $this->message_number_log[] = $message_number;
  3994. 3994                 $this->log_size+= strlen($message);
  3995. 3995                 $this->message_log[] = $message;
  3996. 3996                 while ($this->log_size NET_SSH2_LOG_MAX_SIZE) {
  3997. 3997                     $this->log_size-= strlen(array_shift($this->message_log));
  3998. 3998                     array_shift($this->message_number_log);
  3999. 3999                 }
  4000. 4000                 break;
  4001. 4001             // dump the output out realtime; packets may be interspersed with non packets,
  4002. 4002             // passwords won't be filtered out and select other packets may not be correctly
  4003. 4003             // identified
  4004. 4004             case NET_SSH2_LOG_REALTIME:
  4005. 4005                 switch (PHP_SAPI) {
  4006. 4006                     case 'cli':
  4007. 4007                         $start $stop "\r\n";
  4008. 4008                         break;
  4009. 4009                     default:
  4010. 4010                         $start '<pre>';
  4011. 4011                         $stop '</pre>';
  4012. 4012                 }
  4013. 4013                 echo $start $this->_format_log(array($message), array($message_number)) . $stop;
  4014. 4014                 @flush();
  4015. 4015                 @ob_flush();
  4016. 4016                 break;
  4017. 4017             // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE
  4018. 4018             // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE.
  4019. 4019             // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
  4020. 4020             // at the beginning of the file
  4021. 4021             case NET_SSH2_LOG_REALTIME_FILE:
  4022. 4022                 if (!isset($this->realtime_log_file)) {
  4023. 4023                     // PHP doesn't seem to like using constants in fopen()
  4024. 4024                     $filename NET_SSH2_LOG_REALTIME_FILENAME;
  4025. 4025                     $fp fopen($filename'w');
  4026. 4026                     $this->realtime_log_file $fp;
  4027. 4027                 }
  4028. 4028                 if (!is_resource($this->realtime_log_file)) {
  4029. 4029                     break;
  4030. 4030                 }
  4031. 4031                 $entry $this->_format_log(array($message), array($message_number));
  4032. 4032                 if ($this->realtime_log_wrap) {
  4033. 4033                     $temp "<<< START >>>\r\n";
  4034. 4034                     $entry.= $temp;
  4035. 4035                     fseek($this->realtime_log_fileftell($this->realtime_log_file) - strlen($temp));
  4036. 4036                 }
  4037. 4037                 $this->realtime_log_size+= strlen($entry);
  4038. 4038                 if ($this->realtime_log_size NET_SSH2_LOG_MAX_SIZE) {
  4039. 4039                     fseek($this->realtime_log_file0);
  4040. 4040                     $this->realtime_log_size strlen($entry);
  4041. 4041                     $this->realtime_log_wrap true;
  4042. 4042                 }
  4043. 4043                 fputs($this->realtime_log_file$entry);
  4044. 4044         }
  4045. 4045     }
  4046. 4046 
  4047. 4047     /**
  4048. 4048      * Sends channel data
  4049. 4049      *
  4050. 4050      * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
  4051. 4051      *
  4052. 4052      * @param int $client_channel
  4053. 4053      * @param string $data
  4054. 4054      * @return bool
  4055. 4055      * @access private
  4056. 4056      */
  4057. 4057     function _send_channel_packet($client_channel$data)
  4058. 4058     {
  4059. 4059         while (strlen($data)) {
  4060. 4060             if (!$this->window_size_client_to_server[$client_channel]) {
  4061. 4061                 $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
  4062. 4062                 // using an invalid channel will let the buffers be built up for the valid channels
  4063. 4063                 $this->_get_channel_packet(-1);
  4064. 4064                 $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
  4065. 4065             }
  4066. 4066 
  4067. 4067             /* The maximum amount of data allowed is determined by the maximum
  4068. 4068                packet size for the channel, and the current window size, whichever
  4069. 4069                is smaller.
  4070. 4070                  -- http://tools.ietf.org/html/rfc4254#section-5.2 */
  4071. 4071             $max_size min(
  4072. 4072                 $this->packet_size_client_to_server[$client_channel],
  4073. 4073                 $this->window_size_client_to_server[$client_channel]
  4074. 4074             );
  4075. 4075 
  4076. 4076             $temp $this->_string_shift($data$max_size);
  4077. 4077             $packet pack(
  4078. 4078                 'CN2a*',
  4079. 4079                 NET_SSH2_MSG_CHANNEL_DATA,
  4080. 4080                 $this->server_channels[$client_channel],
  4081. 4081                 strlen($temp),
  4082. 4082                 $temp
  4083. 4083             );
  4084. 4084             $this->window_size_client_to_server[$client_channel]-= strlen($temp);
  4085. 4085             if (!$this->_send_binary_packet($packet)) {
  4086. 4086                 return false;
  4087. 4087             }
  4088. 4088         }
  4089. 4089 
  4090. 4090         return true;
  4091. 4091     }
  4092. 4092 
  4093. 4093     /**
  4094. 4094      * Closes and flushes a channel
  4095. 4095      *
  4096. 4096      * Net_SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
  4097. 4097      * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
  4098. 4098      * for SCP more than anything.
  4099. 4099      *
  4100. 4100      * @param int $client_channel
  4101. 4101      * @param bool $want_reply
  4102. 4102      * @return bool
  4103. 4103      * @access private
  4104. 4104      */
  4105. 4105     function _close_channel($client_channel$want_reply false)
  4106. 4106     {
  4107. 4107         // see http://tools.ietf.org/html/rfc4254#section-5.3
  4108. 4108 
  4109. 4109         $this->_send_binary_packet(pack('CN'NET_SSH2_MSG_CHANNEL_EOF$this->server_channels[$client_channel]));
  4110. 4110 
  4111. 4111         if (!$want_reply) {
  4112. 4112             $this->_send_binary_packet(pack('CN'NET_SSH2_MSG_CHANNEL_CLOSE$this->server_channels[$client_channel]));
  4113. 4113         }
  4114. 4114 
  4115. 4115         $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  4116. 4116 
  4117. 4117         $this->curTimeout 0;
  4118. 4118 
  4119. 4119         while (!is_bool($this->_get_channel_packet($client_channel))) {
  4120. 4120         }
  4121. 4121 
  4122. 4122         if ($want_reply) {
  4123. 4123             $this->_send_binary_packet(pack('CN'NET_SSH2_MSG_CHANNEL_CLOSE$this->server_channels[$client_channel]));
  4124. 4124         }
  4125. 4125 
  4126. 4126         if ($this->bitmap NET_SSH2_MASK_SHELL) {
  4127. 4127             $this->bitmap&= ~NET_SSH2_MASK_SHELL;
  4128. 4128         }
  4129. 4129     }
  4130. 4130 
  4131. 4131     /**
  4132. 4132      * Disconnect
  4133. 4133      *
  4134. 4134      * @param int $reason
  4135. 4135      * @return bool
  4136. 4136      * @access private
  4137. 4137      */
  4138. 4138     function _disconnect($reason)
  4139. 4139     {
  4140. 4140         if ($this->bitmap NET_SSH2_MASK_CONNECTED) {
  4141. 4141             $data pack('CNNa*Na*'NET_SSH2_MSG_DISCONNECT$reason0''0'');
  4142. 4142             $this->_send_binary_packet($data);
  4143. 4143             $this->bitmap 0;
  4144. 4144             fclose($this->fsock);
  4145. 4145             return false;
  4146. 4146         }
  4147. 4147     }
  4148. 4148 
  4149. 4149     /**
  4150. 4150      * String Shift
  4151. 4151      *
  4152. 4152      * Inspired by array_shift
  4153. 4153      *
  4154. 4154      * @param string $string
  4155. 4155      * @param int $index
  4156. 4156      * @return string
  4157. 4157      * @access private
  4158. 4158      */
  4159. 4159     function _string_shift(&$string$index 1)
  4160. 4160     {
  4161. 4161         $substr substr($string0$index);
  4162. 4162         $string substr($string$index);
  4163. 4163         return $substr;
  4164. 4164     }
  4165. 4165 
  4166. 4166     /**
  4167. 4167      * Define Array
  4168. 4168      *
  4169. 4169      * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
  4170. 4170      * named constants from it, using the value as the name of the constant and the index as the value of the constant.
  4171. 4171      * If any of the constants that would be defined already exists, none of the constants will be defined.
  4172. 4172      *
  4173. 4173      * @param array $array
  4174. 4174      * @access private
  4175. 4175      */
  4176. 4176     function _define_array()
  4177. 4177     {
  4178. 4178         $args func_get_args();
  4179. 4179         foreach ($args as $arg) {
  4180. 4180             foreach ($arg as $key => $value) {
  4181. 4181                 if (!defined($value)) {
  4182. 4182                     define($value$key);
  4183. 4183                 } else {
  4184. 4184                     break 2;
  4185. 4185                 }
  4186. 4186             }
  4187. 4187         }
  4188. 4188     }
  4189. 4189 
  4190. 4190     /**
  4191. 4191      * Returns a log of the packets that have been sent and received.
  4192. 4192      *
  4193. 4193      * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
  4194. 4194      *
  4195. 4195      * @access public
  4196. 4196      * @return array|false|string
  4197. 4197      */
  4198. 4198     function getLog()
  4199. 4199     {
  4200. 4200         if (!defined('NET_SSH2_LOGGING')) {
  4201. 4201             return false;
  4202. 4202         }
  4203. 4203 
  4204. 4204         switch (NET_SSH2_LOGGING) {
  4205. 4205             case NET_SSH2_LOG_SIMPLE:
  4206. 4206                 return $this->message_number_log;
  4207. 4207             case NET_SSH2_LOG_COMPLEX:
  4208. 4208                 $log $this->_format_log($this->message_log$this->message_number_log);
  4209. 4209                 return PHP_SAPI == 'cli' $log '<pre>' $log '</pre>';
  4210. 4210             default:
  4211. 4211                 return false;
  4212. 4212         }
  4213. 4213     }
  4214. 4214 
  4215. 4215     /**
  4216. 4216      * Formats a log for printing
  4217. 4217      *
  4218. 4218      * @param array $message_log
  4219. 4219      * @param array $message_number_log
  4220. 4220      * @access private
  4221. 4221      * @return string
  4222. 4222      */
  4223. 4223     function _format_log($message_log$message_number_log)
  4224. 4224     {
  4225. 4225         $output '';
  4226. 4226         for ($i 0$i count($message_log); $i++) {
  4227. 4227             $output.= $message_number_log[$i] . "\r\n";
  4228. 4228             $current_log $message_log[$i];
  4229. 4229             $j 0;
  4230. 4230             do {
  4231. 4231                 if (strlen($current_log)) {
  4232. 4232                     $output.= str_pad(dechex($j), 7'0'STR_PAD_LEFT) . '0  ';
  4233. 4233                 }
  4234. 4234                 $fragment $this->_string_shift($current_log$this->log_short_width);
  4235. 4235                 $hex substr(preg_replace_callback('#.#s', array($this'_format_log_helper'), $fragment), strlen($this->log_boundary));
  4236. 4236                 // replace non ASCII printable characters with dots
  4237. 4237                 // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
  4238. 4238                 // also replace < with a . since < messes up the output on web browsers
  4239. 4239                 $raw preg_replace('#[^\x20-\x7E]|<#''.'$fragment);
  4240. 4240                 $output.= str_pad($hex$this->log_long_width $this->log_short_width' ') . $raw "\r\n";
  4241. 4241                 $j++;
  4242. 4242             } while (strlen($current_log));
  4243. 4243             $output.= "\r\n";
  4244. 4244         }
  4245. 4245 
  4246. 4246         return $output;
  4247. 4247     }
  4248. 4248 
  4249. 4249     /**
  4250. 4250      * Helper function for _format_log
  4251. 4251      *
  4252. 4252      * For use with preg_replace_callback()
  4253. 4253      *
  4254. 4254      * @param array $matches
  4255. 4255      * @access private
  4256. 4256      * @return string
  4257. 4257      */
  4258. 4258     function _format_log_helper($matches)
  4259. 4259     {
  4260. 4260         return $this->log_boundary str_pad(dechex(ord($matches[0])), 2'0'STR_PAD_LEFT);
  4261. 4261     }
  4262. 4262 
  4263. 4263     /**
  4264. 4264      * Helper function for agent->_on_channel_open()
  4265. 4265      *
  4266. 4266      * Used when channels are created to inform agent
  4267. 4267      * of said channel opening. Must be called after
  4268. 4268      * channel open confirmation received
  4269. 4269      *
  4270. 4270      * @access private
  4271. 4271      */
  4272. 4272     function _on_channel_open()
  4273. 4273     {
  4274. 4274         if (isset($this->agent)) {
  4275. 4275             $this->agent->_on_channel_open($this);
  4276. 4276         }
  4277. 4277     }
  4278. 4278 
  4279. 4279     /**
  4280. 4280      * Returns the first value of the intersection of two arrays or false if
  4281. 4281      * the intersection is empty. The order is defined by the first parameter.
  4282. 4282      *
  4283. 4283      * @param array $array1
  4284. 4284      * @param array $array2
  4285. 4285      * @return mixed False if intersection is empty, else intersected value.
  4286. 4286      * @access private
  4287. 4287      */
  4288. 4288     function _array_intersect_first($array1$array2)
  4289. 4289     {
  4290. 4290         foreach ($array1 as $value) {
  4291. 4291             if (in_array($value$array2)) {
  4292. 4292                 return $value;
  4293. 4293             }
  4294. 4294         }
  4295. 4295         return false;
  4296. 4296     }
  4297. 4297 
  4298. 4298     /**
  4299. 4299      * Returns all errors
  4300. 4300      *
  4301. 4301      * @return string[]
  4302. 4302      * @access public
  4303. 4303      */
  4304. 4304     function getErrors()
  4305. 4305     {
  4306. 4306         return $this->errors;
  4307. 4307     }
  4308. 4308 
  4309. 4309     /**
  4310. 4310      * Returns the last error
  4311. 4311      *
  4312. 4312      * @return string
  4313. 4313      * @access public
  4314. 4314      */
  4315. 4315     function getLastError()
  4316. 4316     {
  4317. 4317         $count count($this->errors);
  4318. 4318 
  4319. 4319         if ($count 0) {
  4320. 4320             return $this->errors[$count 1];
  4321. 4321         }
  4322. 4322     }
  4323. 4323 
  4324. 4324     /**
  4325. 4325      * Return the server identification.
  4326. 4326      *
  4327. 4327      * @return string
  4328. 4328      * @access public
  4329. 4329      */
  4330. 4330     function getServerIdentification()
  4331. 4331     {
  4332. 4332         $this->_connect();
  4333. 4333 
  4334. 4334         return $this->server_identifier;
  4335. 4335     }
  4336. 4336 
  4337. 4337     /**
  4338. 4338      * Return a list of the key exchange algorithms the server supports.
  4339. 4339      *
  4340. 4340      * @return array
  4341. 4341      * @access public
  4342. 4342      */
  4343. 4343     function getKexAlgorithms()
  4344. 4344     {
  4345. 4345         $this->_connect();
  4346. 4346 
  4347. 4347         return $this->kex_algorithms;
  4348. 4348     }
  4349. 4349 
  4350. 4350     /**
  4351. 4351      * Return a list of the host key (public key) algorithms the server supports.
  4352. 4352      *
  4353. 4353      * @return array
  4354. 4354      * @access public
  4355. 4355      */
  4356. 4356     function getServerHostKeyAlgorithms()
  4357. 4357     {
  4358. 4358         $this->_connect();
  4359. 4359 
  4360. 4360         return $this->server_host_key_algorithms;
  4361. 4361     }
  4362. 4362 
  4363. 4363     /**
  4364. 4364      * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
  4365. 4365      *
  4366. 4366      * @return array
  4367. 4367      * @access public
  4368. 4368      */
  4369. 4369     function getEncryptionAlgorithmsClient2Server()
  4370. 4370     {
  4371. 4371         $this->_connect();
  4372. 4372 
  4373. 4373         return $this->encryption_algorithms_client_to_server;
  4374. 4374     }
  4375. 4375 
  4376. 4376     /**
  4377. 4377      * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
  4378. 4378      *
  4379. 4379      * @return array
  4380. 4380      * @access public
  4381. 4381      */
  4382. 4382     function getEncryptionAlgorithmsServer2Client()
  4383. 4383     {
  4384. 4384         $this->_connect();
  4385. 4385 
  4386. 4386         return $this->encryption_algorithms_server_to_client;
  4387. 4387     }
  4388. 4388 
  4389. 4389     /**
  4390. 4390      * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
  4391. 4391      *
  4392. 4392      * @return array
  4393. 4393      * @access public
  4394. 4394      */
  4395. 4395     function getMACAlgorithmsClient2Server()
  4396. 4396     {
  4397. 4397         $this->_connect();
  4398. 4398 
  4399. 4399         return $this->mac_algorithms_client_to_server;
  4400. 4400     }
  4401. 4401 
  4402. 4402     /**
  4403. 4403      * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
  4404. 4404      *
  4405. 4405      * @return array
  4406. 4406      * @access public
  4407. 4407      */
  4408. 4408     function getMACAlgorithmsServer2Client()
  4409. 4409     {
  4410. 4410         $this->_connect();
  4411. 4411 
  4412. 4412         return $this->mac_algorithms_server_to_client;
  4413. 4413     }
  4414. 4414 
  4415. 4415     /**
  4416. 4416      * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
  4417. 4417      *
  4418. 4418      * @return array
  4419. 4419      * @access public
  4420. 4420      */
  4421. 4421     function getCompressionAlgorithmsClient2Server()
  4422. 4422     {
  4423. 4423         $this->_connect();
  4424. 4424 
  4425. 4425         return $this->compression_algorithms_client_to_server;
  4426. 4426     }
  4427. 4427 
  4428. 4428     /**
  4429. 4429      * Return a list of the compression algorithms the server supports, when sending stuff to the client.
  4430. 4430      *
  4431. 4431      * @return array
  4432. 4432      * @access public
  4433. 4433      */
  4434. 4434     function getCompressionAlgorithmsServer2Client()
  4435. 4435     {
  4436. 4436         $this->_connect();
  4437. 4437 
  4438. 4438         return $this->compression_algorithms_server_to_client;
  4439. 4439     }
  4440. 4440 
  4441. 4441     /**
  4442. 4442      * Return a list of the languages the server supports, when sending stuff to the client.
  4443. 4443      *
  4444. 4444      * @return array
  4445. 4445      * @access public
  4446. 4446      */
  4447. 4447     function getLanguagesServer2Client()
  4448. 4448     {
  4449. 4449         $this->_connect();
  4450. 4450 
  4451. 4451         return $this->languages_server_to_client;
  4452. 4452     }
  4453. 4453 
  4454. 4454     /**
  4455. 4455      * Return a list of the languages the server supports, when receiving stuff from the client.
  4456. 4456      *
  4457. 4457      * @return array
  4458. 4458      * @access public
  4459. 4459      */
  4460. 4460     function getLanguagesClient2Server()
  4461. 4461     {
  4462. 4462         $this->_connect();
  4463. 4463 
  4464. 4464         return $this->languages_client_to_server;
  4465. 4465     }
  4466. 4466 
  4467. 4467     /**
  4468. 4468      * Returns the banner message.
  4469. 4469      *
  4470. 4470      * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  4471. 4471      * authentication may be relevant for getting legal protection."
  4472. 4472      *
  4473. 4473      * @return string
  4474. 4474      * @access public
  4475. 4475      */
  4476. 4476     function getBannerMessage()
  4477. 4477     {
  4478. 4478         return $this->banner_message;
  4479. 4479     }
  4480. 4480 
  4481. 4481     /**
  4482. 4482      * Returns the server public host key.
  4483. 4483      *
  4484. 4484      * Caching this the first time you connect to a server and checking the result on subsequent connections
  4485. 4485      * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
  4486. 4486      *
  4487. 4487      * @return mixed
  4488. 4488      * @access public
  4489. 4489      */
  4490. 4490     function getServerPublicHostKey()
  4491. 4491     {
  4492. 4492         if (!($this->bitmap NET_SSH2_MASK_CONSTRUCTOR)) {
  4493. 4493             if (!$this->_connect()) {
  4494. 4494                 return false;
  4495. 4495             }
  4496. 4496         }
  4497. 4497 
  4498. 4498         $signature $this->signature;
  4499. 4499         $server_public_host_key $this->server_public_host_key;
  4500. 4500 
  4501. 4501         if (strlen($server_public_host_key) < 4) {
  4502. 4502             return false;
  4503. 4503         }
  4504. 4504         extract(unpack('Nlength'$this->_string_shift($server_public_host_key4)));
  4505. 4505         $this->_string_shift($server_public_host_key$length);
  4506. 4506 
  4507. 4507         if ($this->signature_validated) {
  4508. 4508             return $this->bitmap ?
  4509. 4509                 $this->signature_format ' ' base64_encode($this->server_public_host_key) :
  4510. 4510                 false;
  4511. 4511         }
  4512. 4512 
  4513. 4513         $this->signature_validated true;
  4514. 4514 
  4515. 4515         switch ($this->signature_format) {
  4516. 4516             case 'ssh-dss':
  4517. 4517                 $zero = new Math_BigInteger();
  4518. 4518 
  4519. 4519                 if (strlen($server_public_host_key) < 4) {
  4520. 4520                     return false;
  4521. 4521                 }
  4522. 4522                 $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  4523. 4523                 $p = new Math_BigInteger($this->_string_shift($server_public_host_key$temp['length']), -256);
  4524. 4524 
  4525. 4525                 if (strlen($server_public_host_key) < 4) {
  4526. 4526                     return false;
  4527. 4527                 }
  4528. 4528                 $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  4529. 4529                 $q = new Math_BigInteger($this->_string_shift($server_public_host_key$temp['length']), -256);
  4530. 4530 
  4531. 4531                 if (strlen($server_public_host_key) < 4) {
  4532. 4532                     return false;
  4533. 4533                 }
  4534. 4534                 $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  4535. 4535                 $g = new Math_BigInteger($this->_string_shift($server_public_host_key$temp['length']), -256);
  4536. 4536 
  4537. 4537                 if (strlen($server_public_host_key) < 4) {
  4538. 4538                     return false;
  4539. 4539                 }
  4540. 4540                 $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  4541. 4541                 $y = new Math_BigInteger($this->_string_shift($server_public_host_key$temp['length']), -256);
  4542. 4542 
  4543. 4543                 /* The value for 'dss_signature_blob' is encoded as a string containing
  4544. 4544                    r, followed by s (which are 160-bit integers, without lengths or
  4545. 4545                    padding, unsigned, and in network byte order). */
  4546. 4546                 $temp unpack('Nlength'$this->_string_shift($signature4));
  4547. 4547                 if ($temp['length'] != 40) {
  4548. 4548                     user_error('Invalid signature');
  4549. 4549                     return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  4550. 4550                 }
  4551. 4551 
  4552. 4552                 $r = new Math_BigInteger($this->_string_shift($signature20), 256);
  4553. 4553                 $s = new Math_BigInteger($this->_string_shift($signature20), 256);
  4554. 4554 
  4555. 4555                 switch (true) {
  4556. 4556                     case $r->equals($zero):
  4557. 4557                     case $r->compare($q) >= 0:
  4558. 4558                     case $s->equals($zero):
  4559. 4559                     case $s->compare($q) >= 0:
  4560. 4560                         user_error('Invalid signature');
  4561. 4561                         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  4562. 4562                 }
  4563. 4563 
  4564. 4564                 $w $s->modInverse($q);
  4565. 4565 
  4566. 4566                 $u1 $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
  4567. 4567                 list(, $u1) = $u1->divide($q);
  4568. 4568 
  4569. 4569                 $u2 $w->multiply($r);
  4570. 4570                 list(, $u2) = $u2->divide($q);
  4571. 4571 
  4572. 4572                 $g $g->modPow($u1$p);
  4573. 4573                 $y $y->modPow($u2$p);
  4574. 4574 
  4575. 4575                 $v $g->multiply($y);
  4576. 4576                 list(, $v) = $v->divide($p);
  4577. 4577                 list(, $v) = $v->divide($q);
  4578. 4578 
  4579. 4579                 if (!$v->equals($r)) {
  4580. 4580                     user_error('Bad server signature');
  4581. 4581                     return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4582. 4582                 }
  4583. 4583 
  4584. 4584                 break;
  4585. 4585             case 'ssh-rsa':
  4586. 4586                 if (strlen($server_public_host_key) < 4) {
  4587. 4587                     return false;
  4588. 4588                 }
  4589. 4589                 $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  4590. 4590                 $e = new Math_BigInteger($this->_string_shift($server_public_host_key$temp['length']), -256);
  4591. 4591 
  4592. 4592                 if (strlen($server_public_host_key) < 4) {
  4593. 4593                     return false;
  4594. 4594                 }
  4595. 4595                 $temp unpack('Nlength'$this->_string_shift($server_public_host_key4));
  4596. 4596                 $rawN $this->_string_shift($server_public_host_key$temp['length']);
  4597. 4597                 $n = new Math_BigInteger($rawN, -256);
  4598. 4598                 $nLength strlen(ltrim($rawN"\0"));
  4599. 4599 
  4600. 4600                 /*
  4601. 4601                 if (strlen($signature) < 4) {
  4602. 4602                     return false;
  4603. 4603                 }
  4604. 4604                 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  4605. 4605                 $signature = $this->_string_shift($signature, $temp['length']);
  4606. 4606 
  4607. 4607                 if (!class_exists('Crypt_RSA')) {
  4608. 4608                     include_once 'Crypt/RSA.php';
  4609. 4609                 }
  4610. 4610 
  4611. 4611                 $rsa = new Crypt_RSA();
  4612. 4612                 $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  4613. 4613                 $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
  4614. 4614                 if (!$rsa->verify($this->exchange_hash, $signature)) {
  4615. 4615                     user_error('Bad server signature');
  4616. 4616                     return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4617. 4617                 }
  4618. 4618                 */
  4619. 4619 
  4620. 4620                 if (strlen($signature) < 4) {
  4621. 4621                     return false;
  4622. 4622                 }
  4623. 4623                 $temp unpack('Nlength'$this->_string_shift($signature4));
  4624. 4624                 $s = new Math_BigInteger($this->_string_shift($signature$temp['length']), 256);
  4625. 4625 
  4626. 4626                 // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
  4627. 4627                 // following URL:
  4628. 4628                 // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
  4629. 4629 
  4630. 4630                 // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
  4631. 4631 
  4632. 4632                 if ($s->compare(new Math_BigInteger()) < || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
  4633. 4633                     user_error('Invalid signature');
  4634. 4634                     return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  4635. 4635                 }
  4636. 4636 
  4637. 4637                 $s $s->modPow($e$n);
  4638. 4638                 $s $s->toBytes();
  4639. 4639 
  4640. 4640                 $h pack('N4H*'0x003021300x0906052B0x0E03021A0x05000414sha1($this->exchange_hash));
  4641. 4641                 $h chr(0x01) . str_repeat(chr(0xFF), $nLength strlen($h)) . $h;
  4642. 4642 
  4643. 4643                 if ($s != $h) {
  4644. 4644                     user_error('Bad server signature');
  4645. 4645                     return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4646. 4646                 }
  4647. 4647                 break;
  4648. 4648             default:
  4649. 4649                 user_error('Unsupported signature format');
  4650. 4650                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4651. 4651         }
  4652. 4652 
  4653. 4653         return $this->signature_format ' ' base64_encode($this->server_public_host_key);
  4654. 4654     }
  4655. 4655 
  4656. 4656     /**
  4657. 4657      * Returns the exit status of an SSH command or false.
  4658. 4658      *
  4659. 4659      * @return false|int
  4660. 4660      * @access public
  4661. 4661      */
  4662. 4662     function getExitStatus()
  4663. 4663     {
  4664. 4664         if (is_null($this->exit_status)) {
  4665. 4665             return false;
  4666. 4666         }
  4667. 4667         return $this->exit_status;
  4668. 4668     }
  4669. 4669 
  4670. 4670     /**
  4671. 4671      * Returns the number of columns for the terminal window size.
  4672. 4672      *
  4673. 4673      * @return int
  4674. 4674      * @access public
  4675. 4675      */
  4676. 4676     function getWindowColumns()
  4677. 4677     {
  4678. 4678         return $this->windowColumns;
  4679. 4679     }
  4680. 4680 
  4681. 4681     /**
  4682. 4682      * Returns the number of rows for the terminal window size.
  4683. 4683      *
  4684. 4684      * @return int
  4685. 4685      * @access public
  4686. 4686      */
  4687. 4687     function getWindowRows()
  4688. 4688     {
  4689. 4689         return $this->windowRows;
  4690. 4690     }
  4691. 4691 
  4692. 4692     /**
  4693. 4693      * Sets the number of columns for the terminal window size.
  4694. 4694      *
  4695. 4695      * @param int $value
  4696. 4696      * @access public
  4697. 4697      */
  4698. 4698     function setWindowColumns($value)
  4699. 4699     {
  4700. 4700         $this->windowColumns $value;
  4701. 4701     }
  4702. 4702 
  4703. 4703     /**
  4704. 4704      * Sets the number of rows for the terminal window size.
  4705. 4705      *
  4706. 4706      * @param int $value
  4707. 4707      * @access public
  4708. 4708      */
  4709. 4709     function setWindowRows($value)
  4710. 4710     {
  4711. 4711         $this->windowRows $value;
  4712. 4712     }
  4713. 4713 
  4714. 4714     /**
  4715. 4715      * Sets the number of columns and rows for the terminal window size.
  4716. 4716      *
  4717. 4717      * @param int $columns
  4718. 4718      * @param int $rows
  4719. 4719      * @access public
  4720. 4720      */
  4721. 4721     function setWindowSize($columns 80$rows 24)
  4722. 4722     {
  4723. 4723         $this->windowColumns $columns;
  4724. 4724         $this->windowRows $rows;
  4725. 4725     }
  4726. 4726 }